summaryrefslogtreecommitdiffstats
path: root/security-utils
diff options
context:
space:
mode:
authorTor Brede Vekterli <vekterli@yahooinc.com>2022-11-11 12:58:46 +0100
committerTor Brede Vekterli <vekterli@yahooinc.com>2022-11-11 13:17:28 +0100
commit7855a0d37241e87afe514ec25bf7a00289b556d7 (patch)
treec21ced3800c3f846cdd55bcf5c15355330a7eb42 /security-utils
parentf2ce165982217902ea84bcb12e7a10fe008bacd4 (diff)
Add support for token resealing
Adds underlying support--and tooling--for resealing a token for another recipient. This allows for delegating decryption to another party without having to reveal the private key of the original recipient (or having to send the raw underlying secret key over a potentially insecure channel). Key ID can/should change as part of this operation.
Diffstat (limited to 'security-utils')
-rw-r--r--security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java16
-rw-r--r--security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java16
2 files changed, 28 insertions, 4 deletions
diff --git a/security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java b/security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java
index 8a1a7dd3688..66a87a94707 100644
--- a/security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java
+++ b/security-utils/src/main/java/com/yahoo/security/SharedKeyGenerator.java
@@ -62,10 +62,7 @@ public class SharedKeyGenerator {
public static SecretSharedKey generateForReceiverPublicKey(PublicKey receiverPublicKey, KeyId keyId) {
var secretKey = generateRandomSecretAesKey();
- // We protect the integrity of the key ID by passing it as AAD.
- var sealed = HPKE.sealBase((XECPublicKey) receiverPublicKey, EMPTY_BYTES, keyId.asBytes(), secretKey.getEncoded());
- var sealedSharedKey = new SealedSharedKey(keyId, sealed.enc(), sealed.ciphertext());
- return new SecretSharedKey(secretKey, sealedSharedKey);
+ return internalSealSecretKeyForReceiver(secretKey, receiverPublicKey, keyId);
}
public static SecretSharedKey fromSealedKey(SealedSharedKey sealedKey, PrivateKey receiverPrivateKey) {
@@ -74,6 +71,17 @@ public class SharedKeyGenerator {
return new SecretSharedKey(new SecretKeySpec(secretKeyBytes, "AES"), sealedKey);
}
+ public static SecretSharedKey reseal(SecretSharedKey secret, PublicKey receiverPublicKey, KeyId keyId) {
+ return internalSealSecretKeyForReceiver(secret.secretKey(), receiverPublicKey, keyId);
+ }
+
+ private static SecretSharedKey internalSealSecretKeyForReceiver(SecretKey secretKey, PublicKey receiverPublicKey, KeyId keyId) {
+ // We protect the integrity of the key ID by passing it as AAD.
+ var sealed = HPKE.sealBase((XECPublicKey) receiverPublicKey, EMPTY_BYTES, keyId.asBytes(), secretKey.getEncoded());
+ var sealedSharedKey = new SealedSharedKey(keyId, sealed.enc(), sealed.ciphertext());
+ return new SecretSharedKey(secretKey, sealedSharedKey);
+ }
+
// A given key+IV pair can only be used for one single encryption session, ever.
// Since our keys are intended to be inherently single-use, we can satisfy that
// requirement even with a fixed IV. This avoids the need for explicitly including
diff --git a/security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java b/security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java
index 4e64bc3e9aa..23e22345cc6 100644
--- a/security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java
+++ b/security-utils/src/test/java/com/yahoo/security/SharedKeyTest.java
@@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
public class SharedKeyTest {
private static final KeyId KEY_ID_1 = KeyId.ofString("1");
+ private static final KeyId KEY_ID_2 = KeyId.ofString("2");
@Test
void generated_secret_key_is_128_bit_aes() {
@@ -45,6 +46,21 @@ public class SharedKeyTest {
}
@Test
+ void secret_key_can_be_resealed_for_another_receiver() {
+ var originalReceiverKp = KeyUtils.generateX25519KeyPair();
+ var secondaryReceiverKp = KeyUtils.generateX25519KeyPair();
+ var myShared = SharedKeyGenerator.generateForReceiverPublicKey(originalReceiverKp.getPublic(), KEY_ID_1);
+ var theirShared = SharedKeyGenerator.reseal(myShared, secondaryReceiverKp.getPublic(), KEY_ID_2);
+
+ var publicToken = theirShared.sealedSharedKey().toTokenString();
+ var theirSealed = SealedSharedKey.fromTokenString(publicToken);
+ assertEquals(KEY_ID_2, theirSealed.keyId());
+ theirShared = SharedKeyGenerator.fromSealedKey(theirSealed, secondaryReceiverKp.getPrivate());
+ // Should be same internal secret key
+ assertArrayEquals(myShared.secretKey().getEncoded(), theirShared.secretKey().getEncoded());
+ }
+
+ @Test
void token_v1_representation_is_stable() {
var receiverPrivate = KeyUtils.fromBase58EncodedX25519PrivateKey("GFg54SaGNCmcSGufZCx68SKLGuAFrASoDeMk3t5AjU6L");
var receiverPublic = KeyUtils.fromBase58EncodedX25519PublicKey( "5drrkakYLjYSBpr5Haknh13EiCYL36ndMzK4gTJo6pwh");