Skip to content

Commit 16c9356

Browse files
tholenstcopybara-github
authored andcommitted
Add a helper class which we might use in the future to provide HPKE backed by Android Keystore.
PiperOrigin-RevId: 763694768 Change-Id: I395c3373c053f4a4b370412c2abf1c301b12aacf
1 parent 00de94d commit 16c9356

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

src/main/java/com/google/crypto/tink/hybrid/internal/AuthHpkeHelperForAndroidKeystore.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
import com.google.crypto.tink.hybrid.HpkePublicKey;
2222
import com.google.crypto.tink.subtle.Bytes;
2323
import com.google.crypto.tink.subtle.EllipticCurves;
24+
import com.google.crypto.tink.subtle.EllipticCurves.PointFormatType;
2425
import com.google.errorprone.annotations.Immutable;
2526
import java.security.GeneralSecurityException;
27+
import java.security.spec.ECPoint;
2628

2729
/**
2830
* A class with functions helping for HPKE implementations based on Android Keystore.
@@ -115,7 +117,7 @@ private static void validateParameters(HpkeParameters parameters)
115117
* <p>The ciphertext must have been encrypted with the public key used to create this helper
116118
* object. The encapsulated key must be in encapsulatedKey. dhSharedSecret1 must be the
117119
* Diffie-Hellman shared secrets computed between the receiver and encapsulated key,
118-
* dhSharedSecret2 must be the Diffie-Hellman secret between the reciever and the sender key.
120+
* dhSharedSecret2 must be the Diffie-Hellman secret between the receiver and the sender key.
119121
*/
120122
public byte[] decryptAuthenticatedWithEncapsulatedKeyAndP256SharedSecret(
121123
byte[] encapsulatedKey,
@@ -135,4 +137,44 @@ public byte[] decryptAuthenticatedWithEncapsulatedKeyAndP256SharedSecret(
135137
HpkeUtil.AUTH_MODE, encapsulatedKey, derivedSharedSecret, kem, kdf, aead, info);
136138
return context.open(ciphertext, ciphertextOffset, EMPTY_ASSOCIATED_DATA);
137139
}
140+
141+
/**
142+
* Encrypts a message.
143+
*
144+
* <p>The message will be encrypted for `theirPublicKeyByteArray` and authenticated with
145+
* `ourPublicKey`. The value in emphemeralPublicKey must contain the public key piont of an
146+
* ephemerally generated key. dhSharedSecret1 must be the Diffie-Hellman shared secrets computed
147+
* between the receiver and emphemeralKey. dhSharedSecret2 must be the Diffie-Hellman secret
148+
* between the receiver and the sender key.
149+
*/
150+
public byte[] encryptAuthenticatedWithEncapsulatedKeyAndP256SharedSecret(
151+
ECPoint emphemeralPublicKey,
152+
byte[] dhSharedSecret1,
153+
byte[] dhSharedSecret2,
154+
byte[] plaintext,
155+
byte[] contextInfo)
156+
throws GeneralSecurityException {
157+
byte[] emphemeralPublicKeyByteArray =
158+
EllipticCurves.pointEncode(
159+
EllipticCurves.CurveType.NIST_P256, PointFormatType.UNCOMPRESSED, emphemeralPublicKey);
160+
byte[] dhSharedSecret = Bytes.concat(dhSharedSecret1, dhSharedSecret2);
161+
byte[] derivedSharedSecret =
162+
NistCurvesHpkeKem.fromCurve(EllipticCurves.CurveType.NIST_P256)
163+
.deriveKemSharedSecret(
164+
dhSharedSecret,
165+
emphemeralPublicKeyByteArray,
166+
theirPublicKeyByteArray,
167+
ourPublicKeyByteArray);
168+
HpkeContext context =
169+
HpkeContext.createContext(
170+
HpkeUtil.AUTH_MODE,
171+
emphemeralPublicKeyByteArray,
172+
derivedSharedSecret,
173+
kem,
174+
kdf,
175+
aead,
176+
contextInfo);
177+
return Bytes.concat(
178+
emphemeralPublicKeyByteArray, context.seal(plaintext, EMPTY_ASSOCIATED_DATA));
179+
}
138180
}

src/test/java/com/google/crypto/tink/hybrid/internal/AuthHpkeHelperForAndroidKeystoreTest.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,57 @@ public void invalidParamsDifferent_create_throws() throws Exception {
248248
() -> AuthHpkeHelperForAndroidKeystore.create(ourPublicKey, theirPublicKey));
249249
assertThat(e).hasMessageThat().contains("must be equal");
250250
}
251+
252+
@Test
253+
public void encryptAuthenticatedWithEncapsulatedKeyAndP256SharedSecret_testVector_success()
254+
throws Exception {
255+
HpkeParameters params =
256+
HpkeParameters.builder()
257+
.setVariant(HpkeParameters.Variant.NO_PREFIX)
258+
.setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
259+
.setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
260+
.setAeadId(HpkeParameters.AeadId.AES_128_GCM)
261+
.build();
262+
// The same test vector as above, but we now encrypt, so ourPublicKeyMaterial is now theirs.
263+
byte[] ourPublicKeyMaterial =
264+
Hex.decode(
265+
"04f727e47e7bd3ba6e93ac5898f2e0a78e4079d573195bbe2c22eb4b8b679361afe10a0dc3a59e0bd49736"
266+
+ "206f26ffc55b830fb61e49a58b11b16cda3636f72eb6");
267+
byte[] theirPublicKeyMaterial =
268+
Hex.decode(
269+
"04f873fef6483b6c59e3b125cfd824a25068ff3fed93245da71c2c0a843a9de3bcaac4bd8309e67d4d2115"
270+
+ "ac74b6cde00d2f5c4ea4caf709462ddb3e66d7439a89");
271+
byte[] encapsulatedKey =
272+
Hex.decode(
273+
"04721adb65bc9d99aeabb5e2b2705979c9c4bc4110a252f784bf7190527625d34021c3338e59c8b86720e3"
274+
+ "fecb2475ba538ebeb1a5b0eef729ec8add7c7fba9634");
275+
ECPoint encapsulatedKeyAsObject =
276+
EllipticCurves.pointDecode(
277+
EllipticCurves.CurveType.NIST_P256, PointFormatType.UNCOMPRESSED, encapsulatedKey);
278+
byte[] dhSharedSecret1 =
279+
Hex.decode("c1ecd8f496f1e9babcfb616460780efac5755de1b59375bd8887cadc2871a173");
280+
byte[] dhSharedSecret2 =
281+
Hex.decode("6d72987cf8cab371f22a13fcc2ac9fd53494a600f761fe839946346ee5633149");
282+
byte[] contextInfo = Hex.decode("e97f8ccce315e82e2013");
283+
HpkePublicKey theirPublicKey =
284+
HpkePublicKey.create(
285+
params, Bytes.copyFrom(theirPublicKeyMaterial), /* idRequirement= */ null);
286+
HpkePublicKey ourPublicKey =
287+
HpkePublicKey.create(
288+
params, Bytes.copyFrom(ourPublicKeyMaterial), /* idRequirement= */ null);
289+
AuthHpkeHelperForAndroidKeystore helper =
290+
AuthHpkeHelperForAndroidKeystore.create(ourPublicKey, theirPublicKey);
291+
assertThat(
292+
Hex.encode(
293+
helper.encryptAuthenticatedWithEncapsulatedKeyAndP256SharedSecret(
294+
encapsulatedKeyAsObject,
295+
dhSharedSecret1,
296+
dhSharedSecret2,
297+
Hex.decode("92b2058b295b7746202d"),
298+
contextInfo)))
299+
.isEqualTo(
300+
"04721adb65bc9d99aeabb5e2b2705979c9c4bc4110a252f784bf7190527625d34021c3338e59c8b86720e3"
301+
+ "fecb2475ba538ebeb1a5b0eef729ec8add7c7fba96349f7c248adec683db2e4140ef3d0c201e146b"
302+
+ "96439042402a6b3d");
303+
}
251304
}

0 commit comments

Comments
 (0)