1 /** 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * SPDX-License-Identifier: Apache-2.0. 4 */ 5 package software.amazon.awssdk.crt.io; 6 7 import java.util.HashMap; 8 import java.util.Map; 9 import java.io.StringWriter; 10 import java.io.PrintWriter; 11 12 import software.amazon.awssdk.crt.CrtResource; 13 import software.amazon.awssdk.crt.Log; 14 import software.amazon.awssdk.crt.Log.LogLevel; 15 import software.amazon.awssdk.crt.Log.LogSubject; 16 17 /** 18 * A class containing a mutual TLS (mTLS) Private Key operation that needs to be performed. 19 * This class is passed to TlsKeyOperationHandler if a custom key operation is set. 20 * 21 * You MUST call either complete(output) or completeExceptionally(exception) 22 * or the TLS connection will hang forever! 23 */ 24 public final class TlsKeyOperation { 25 26 /** 27 * The type of TlsKeyOperation that needs to be performed by the TlsKeyOperationHandler interface. 28 */ 29 public enum Type { 30 UNKNOWN(0), SIGN(1), DECRYPT(2); 31 buildEnumMapping()32 static Map<Integer, Type> buildEnumMapping() { 33 Map<Integer, Type> enumMapping = new HashMap<Integer, Type>(); 34 for (Type i : Type.values()) { 35 enumMapping.put(i.nativeValue, i); 36 } 37 return enumMapping; 38 } 39 getEnumValueFromInteger(int value)40 public static Type getEnumValueFromInteger(int value) { 41 Type enumValue = enumMapping.get(value); 42 if (enumValue != null) { 43 return enumValue; 44 } 45 throw new RuntimeException("Illegal TlsKeyOperation.Type"); 46 } 47 Type(int nativeValue)48 Type(int nativeValue) { 49 this.nativeValue = nativeValue; 50 } 51 getNativeValue()52 public int getNativeValue() { 53 return nativeValue; 54 } 55 56 int nativeValue; 57 static Map<Integer, Type> enumMapping = buildEnumMapping(); 58 } 59 60 private CrtResourceInternal nativeResource; 61 private byte[] inputData; 62 private Type operationType; 63 private TlsSignatureAlgorithm signatureAlgorithm; 64 private TlsHashAlgorithm digestAlgorithm; 65 66 /* Called from native when there's a new operation to be performed. Creates a new TlsKeyOperation */ TlsKeyOperation(long nativeHandle, byte[] inputData, int operationType, int signatureAlgorithm, int digestAlgorithm)67 protected TlsKeyOperation(long nativeHandle, byte[] inputData, int operationType, int signatureAlgorithm, 68 int digestAlgorithm) { 69 70 nativeResource = new CrtResourceInternal(nativeHandle); 71 this.inputData = inputData; 72 this.operationType = Type.getEnumValueFromInteger(operationType); 73 this.signatureAlgorithm = TlsSignatureAlgorithm.getEnumValueFromInteger(signatureAlgorithm); 74 this.digestAlgorithm = TlsHashAlgorithm.getEnumValueFromInteger(digestAlgorithm); 75 } 76 77 /** 78 * Returns the input data from native that needs to be operated on using the private key. 79 * You can determine the operation that needs to be performed on the data using the getType function. 80 * 81 * @return The input data from native that needs to be operated on 82 */ getInput()83 public byte[] getInput() { 84 return inputData; 85 } 86 87 /** 88 * Returns the operation that needs to be performed. 89 * 90 * @return The operation that needs to be performed. 91 */ getType()92 public Type getType() { 93 return operationType; 94 } 95 96 /** 97 * Returns the TLS algorithm used in the signature. 98 * 99 * @return The TLS algorithm used in the signature 100 */ getSignatureAlgorithm()101 public TlsSignatureAlgorithm getSignatureAlgorithm() { 102 return signatureAlgorithm; 103 } 104 105 /** 106 * Returns the TLS Hash algorithm used in the digest. 107 * 108 * @return The TLS Hash algorithm used in the digest 109 */ getDigestAlgorithm()110 public TlsHashAlgorithm getDigestAlgorithm() { 111 return digestAlgorithm; 112 } 113 114 /** 115 * The function to call when you have modified the input data using the private key and are ready to 116 * return it for use in the mutual TLS Handshake. 117 * 118 * @param output The modified input data that has been modified by the custom key operation 119 */ complete(byte[] output)120 public synchronized void complete(byte[] output) { 121 if (nativeResource.isNull()) { 122 Log.log(LogLevel.Error, LogSubject.CommonGeneral, 123 "No native handle set in TlsKeyOperation! Cannot complete operation"); 124 return; 125 } 126 127 tlsKeyOperationComplete(nativeResource.getNativeHandle(), output); 128 nativeResource.close(); 129 } 130 131 /** 132 * The function to call when you either have an exception and want to complete the operation with an 133 * exception or you cannot complete the operation. This will mark the operation as complete with an 134 * exception so it can be reacted to accordingly. 135 * 136 * @param ex The exeception to complete with 137 */ completeExceptionally(Throwable ex)138 public synchronized void completeExceptionally(Throwable ex) { 139 if (nativeResource.isNull()) { 140 Log.log(LogLevel.Error, LogSubject.CommonGeneral, 141 "No native handle set in TlsKeyOperation! Cannot complete operation exceptionally"); 142 return; 143 } 144 145 tlsKeyOperationCompleteExceptionally(nativeResource.getNativeHandle(), ex); 146 nativeResource.close(); 147 } 148 149 /** 150 * The TlsKeyOperation has special lifetime rules, where you have to call one of the complete functions, and 151 * by using this private, internal-only CRT resource, we can still get the benefits of using a CRT resource 152 * for detecting memory leaks, while not exposing functionality that would conflict with the lifetime rules. 153 */ 154 private class CrtResourceInternal extends CrtResource { CrtResourceInternal(long nativeHandle)155 CrtResourceInternal(long nativeHandle) { 156 acquireNativeHandle(nativeHandle); 157 } 158 releaseNativeHandle()159 protected void releaseNativeHandle() {} 160 canReleaseReferencesImmediately()161 protected boolean canReleaseReferencesImmediately() { 162 return true; 163 }; 164 } 165 invokePerformOperation(TlsKeyOperationHandler handler, TlsKeyOperation operation)166 static private void invokePerformOperation(TlsKeyOperationHandler handler, TlsKeyOperation operation) { 167 try { 168 handler.performOperation(operation); 169 } catch (Exception ex) { 170 /** 171 * printStackTrace gives a nice, full picture of the exception 172 * but to use it, we have to use a StringWriter and a PrintWriter 173 */ 174 StringWriter stringWriter = new StringWriter(); 175 ex.printStackTrace(new PrintWriter(stringWriter)); 176 Log.log(LogLevel.Error, LogSubject.CommonGeneral, 177 "Exception occured!\n" + stringWriter.toString()); 178 179 operation.completeExceptionally(ex); 180 } 181 } 182 183 /******************************************************************************* 184 * native methods 185 ******************************************************************************/ tlsKeyOperationComplete(long nativeHandle, byte[] output)186 private static native void tlsKeyOperationComplete(long nativeHandle, byte[] output); tlsKeyOperationCompleteExceptionally(long nativeHandle, Throwable ex)187 private static native void tlsKeyOperationCompleteExceptionally(long nativeHandle, Throwable ex); 188 189 } 190 191