1 // Copyright 2017 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.CryptoFormat; 20 import com.google.crypto.tink.PublicKeyVerify; 21 import com.google.crypto.tink.internal.LegacyProtoKey; 22 import com.google.crypto.tink.internal.MonitoringClient; 23 import com.google.crypto.tink.internal.MonitoringKeysetInfo; 24 import com.google.crypto.tink.internal.MonitoringUtil; 25 import com.google.crypto.tink.internal.MutableMonitoringRegistry; 26 import com.google.crypto.tink.internal.MutablePrimitiveRegistry; 27 import com.google.crypto.tink.internal.PrimitiveConstructor; 28 import com.google.crypto.tink.internal.PrimitiveRegistry; 29 import com.google.crypto.tink.internal.PrimitiveSet; 30 import com.google.crypto.tink.internal.PrimitiveWrapper; 31 import com.google.crypto.tink.signature.internal.LegacyFullVerify; 32 import java.security.GeneralSecurityException; 33 import java.util.Arrays; 34 import java.util.List; 35 36 /** 37 * The implementation of {@code PrimitiveWrapper<DeterministicAead>}. 38 * 39 * <p>The returned primitive works with a keyset (rather than a single key). To verify a signature, 40 * the primitive uses the prefix of the signature to efficiently select the right key in the set. If 41 * there is no key associated with the prefix or if the keys associated with the prefix do not work, 42 * the primitive tries all keys with {@link com.google.crypto.tink.proto.OutputPrefixType#RAW}. 43 * 44 * @since 1.0.0 45 */ 46 public class PublicKeyVerifyWrapper implements PrimitiveWrapper<PublicKeyVerify, PublicKeyVerify> { 47 48 private static final PublicKeyVerifyWrapper WRAPPER = new PublicKeyVerifyWrapper(); 49 private static final PrimitiveConstructor<LegacyProtoKey, PublicKeyVerify> 50 LEGACY_PRIMITIVE_CONSTRUCTOR = 51 PrimitiveConstructor.create( 52 LegacyFullVerify::create, LegacyProtoKey.class, PublicKeyVerify.class); 53 54 private static class WrappedPublicKeyVerify implements PublicKeyVerify { 55 private final PrimitiveSet<PublicKeyVerify> primitives; 56 57 private final MonitoringClient.Logger monitoringLogger; 58 WrappedPublicKeyVerify(PrimitiveSet<PublicKeyVerify> primitives)59 public WrappedPublicKeyVerify(PrimitiveSet<PublicKeyVerify> primitives) { 60 this.primitives = primitives; 61 if (primitives.hasAnnotations()) { 62 MonitoringClient client = MutableMonitoringRegistry.globalInstance().getMonitoringClient(); 63 MonitoringKeysetInfo keysetInfo = MonitoringUtil.getMonitoringKeysetInfo(primitives); 64 this.monitoringLogger = client.createLogger(keysetInfo, "public_key_verify", "verify"); 65 } else { 66 this.monitoringLogger = MonitoringUtil.DO_NOTHING_LOGGER; 67 } 68 } 69 70 @Override verify(final byte[] signature, final byte[] data)71 public void verify(final byte[] signature, final byte[] data) throws GeneralSecurityException { 72 if (signature.length <= CryptoFormat.NON_RAW_PREFIX_SIZE) { 73 // This also rejects raw signatures with size of 4 bytes or fewer. We're not aware of any 74 // schemes that output signatures that small. 75 monitoringLogger.logFailure(); 76 throw new GeneralSecurityException("signature too short"); 77 } 78 byte[] prefix = Arrays.copyOf(signature, CryptoFormat.NON_RAW_PREFIX_SIZE); 79 List<PrimitiveSet.Entry<PublicKeyVerify>> entries = primitives.getPrimitive(prefix); 80 for (PrimitiveSet.Entry<PublicKeyVerify> entry : entries) { 81 try { 82 entry.getFullPrimitive().verify(signature, data); 83 monitoringLogger.log(entry.getKeyId(), data.length); 84 // If there is no exception, the signature is valid and we can return. 85 return; 86 } catch (GeneralSecurityException e) { 87 // Ignored as we want to continue verification with the remaining keys. 88 } 89 } 90 91 // None "non-raw" key matched, so let's try the raw keys (if any exist). 92 entries = primitives.getRawPrimitives(); 93 for (PrimitiveSet.Entry<PublicKeyVerify> entry : entries) { 94 try { 95 entry.getFullPrimitive().verify(signature, data); 96 monitoringLogger.log(entry.getKeyId(), data.length); 97 // If there is no exception, the signature is valid and we can return. 98 return; 99 } catch (GeneralSecurityException e) { 100 // Ignored as we want to continue verification with raw keys. 101 } 102 } 103 // nothing works. 104 monitoringLogger.logFailure(); 105 throw new GeneralSecurityException("invalid signature"); 106 } 107 } 108 109 @Override wrap(final PrimitiveSet<PublicKeyVerify> primitives)110 public PublicKeyVerify wrap(final PrimitiveSet<PublicKeyVerify> primitives) { 111 return new WrappedPublicKeyVerify(primitives); 112 } 113 114 @Override getPrimitiveClass()115 public Class<PublicKeyVerify> getPrimitiveClass() { 116 return PublicKeyVerify.class; 117 } 118 119 @Override getInputPrimitiveClass()120 public Class<PublicKeyVerify> getInputPrimitiveClass() { 121 return PublicKeyVerify.class; 122 } 123 124 /** 125 * Register the wrapper within the registry. 126 * 127 * <p>This is required for calls to {@link Keyset.getPrimitive} with a {@link PublicKeyVerify} 128 * argument. 129 */ register()130 static void register() throws GeneralSecurityException { 131 MutablePrimitiveRegistry.globalInstance().registerPrimitiveWrapper(WRAPPER); 132 MutablePrimitiveRegistry.globalInstance() 133 .registerPrimitiveConstructor(LEGACY_PRIMITIVE_CONSTRUCTOR); 134 } 135 136 /** 137 * registerToInternalPrimitiveRegistry is a non-public method (it takes an argument of an 138 * internal-only type) registering an instance of {@code PublicKeyVerifyWrapper} to the provided 139 * {@code PrimitiveRegistry#Builder}. 140 */ registerToInternalPrimitiveRegistry( PrimitiveRegistry.Builder primitiveRegistryBuilder)141 public static void registerToInternalPrimitiveRegistry( 142 PrimitiveRegistry.Builder primitiveRegistryBuilder) throws GeneralSecurityException { 143 primitiveRegistryBuilder.registerPrimitiveWrapper(WRAPPER); 144 } 145 } 146