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 static com.google.crypto.tink.internal.Util.isPrefix; 20 21 import com.google.crypto.tink.AccessesPartialKey; 22 import com.google.crypto.tink.HybridDecrypt; 23 import com.google.crypto.tink.InsecureSecretKeyAccess; 24 import com.google.crypto.tink.hybrid.EciesPrivateKey; 25 import com.google.crypto.tink.hybrid.internal.EciesDemHelper; 26 import com.google.crypto.tink.internal.BigIntegerEncoding; 27 import java.security.GeneralSecurityException; 28 import java.security.interfaces.ECPrivateKey; 29 import java.security.spec.EllipticCurve; 30 import java.util.Arrays; 31 32 /** 33 * ECIES encryption with HKDF-KEM (key encapsulation mechanism) and AEAD-DEM (data encapsulation 34 * mechanism). 35 * 36 * @since 1.0.0 37 */ 38 public final class EciesAeadHkdfHybridDecrypt implements HybridDecrypt { 39 private final ECPrivateKey recipientPrivateKey; 40 private final EciesHkdfRecipientKem recipientKem; 41 private final String hkdfHmacAlgo; 42 private final byte[] hkdfSalt; 43 private final EllipticCurves.PointFormatType ecPointFormat; 44 private final EciesDemHelper.Dem dem; 45 private final byte[] outputPrefix; 46 EciesAeadHkdfHybridDecrypt( final ECPrivateKey recipientPrivateKey, final byte[] hkdfSalt, String hkdfHmacAlgo, EllipticCurves.PointFormatType ecPointFormat, EciesDemHelper.Dem dem, byte[] outputPrefix)47 private EciesAeadHkdfHybridDecrypt( 48 final ECPrivateKey recipientPrivateKey, 49 final byte[] hkdfSalt, 50 String hkdfHmacAlgo, 51 EllipticCurves.PointFormatType ecPointFormat, 52 EciesDemHelper.Dem dem, 53 byte[] outputPrefix) { 54 this.recipientPrivateKey = recipientPrivateKey; 55 this.recipientKem = new EciesHkdfRecipientKem(recipientPrivateKey); 56 this.hkdfSalt = hkdfSalt; 57 this.hkdfHmacAlgo = hkdfHmacAlgo; 58 this.ecPointFormat = ecPointFormat; 59 this.dem = dem; 60 this.outputPrefix = outputPrefix; 61 } 62 63 @AccessesPartialKey create(EciesPrivateKey key)64 public static HybridDecrypt create(EciesPrivateKey key) throws GeneralSecurityException { 65 EllipticCurves.CurveType curveType = 66 EciesAeadHkdfHybridEncrypt.CURVE_TYPE_CONVERTER.toProtoEnum( 67 key.getParameters().getCurveType()); 68 ECPrivateKey recipientPrivateKey = 69 EllipticCurves.getEcPrivateKey( 70 curveType, 71 BigIntegerEncoding.toBigEndianBytes( 72 key.getNistPrivateKeyValue().getBigInteger(InsecureSecretKeyAccess.get()))); 73 byte[] hkdfSalt = new byte[0]; 74 if (key.getParameters().getSalt() != null) { 75 hkdfSalt = key.getParameters().getSalt().toByteArray(); 76 } 77 return new EciesAeadHkdfHybridDecrypt( 78 recipientPrivateKey, 79 hkdfSalt, 80 EciesAeadHkdfHybridEncrypt.toHmacAlgo(key.getParameters().getHashType()), 81 EciesAeadHkdfHybridEncrypt.POINT_FORMAT_TYPE_CONVERTER.toProtoEnum( 82 key.getParameters().getNistCurvePointFormat()), 83 EciesDemHelper.getDem(key.getParameters()), 84 key.getOutputPrefix().toByteArray()); 85 } 86 87 @Override decrypt(final byte[] ciphertext, final byte[] contextInfo)88 public byte[] decrypt(final byte[] ciphertext, final byte[] contextInfo) 89 throws GeneralSecurityException { 90 if (!isPrefix(outputPrefix, ciphertext)) { 91 throw new GeneralSecurityException("Invalid ciphertext (output prefix mismatch)"); 92 } 93 int prefixSize = outputPrefix.length; 94 EllipticCurve curve = recipientPrivateKey.getParams().getCurve(); 95 int headerSize = EllipticCurves.encodingSizeInBytes(curve, ecPointFormat); 96 if (ciphertext.length < prefixSize + headerSize) { 97 throw new GeneralSecurityException("ciphertext too short"); 98 } 99 byte[] kemBytes = Arrays.copyOfRange(ciphertext, prefixSize, prefixSize + headerSize); 100 byte[] symmetricKey = 101 recipientKem.generateKey( 102 kemBytes, 103 hkdfHmacAlgo, 104 hkdfSalt, 105 contextInfo, 106 dem.getSymmetricKeySizeInBytes(), 107 ecPointFormat); 108 return dem.decrypt(symmetricKey, ciphertext, prefixSize + headerSize); 109 } 110 } 111