summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Brede Vekterli <vekterli@yahooinc.com>2022-10-19 13:16:29 +0200
committerGitHub <noreply@github.com>2022-10-19 13:16:29 +0200
commitfe64bb97196040f633a9d1da5c83d808f9c324dd (patch)
tree50c60b33863830370bab4d17e3babb66e0f4398f
parent4c2b3a8ce6116c50cb20c560be08e37490efb1ad (diff)
parent82c8d614762c3e4bb0abc14148a1fba1ca3182e5 (diff)
Merge pull request #24504 from vespa-engine/vekterli/only-require-private-key-for-seal-open
Add X25519 private to public key extraction and use for HPKE opening
-rw-r--r--security-utils/src/main/java/com/yahoo/security/KeyUtils.java9
-rw-r--r--security-utils/src/main/java/com/yahoo/security/hpke/DHKemX25519HkdfSha256.java9
-rw-r--r--security-utils/src/main/java/com/yahoo/security/hpke/Hpke.java11
-rw-r--r--security-utils/src/main/java/com/yahoo/security/hpke/Kem.java5
-rw-r--r--security-utils/src/test/java/com/yahoo/security/HpkeTest.java27
-rw-r--r--security-utils/src/test/java/com/yahoo/security/KeyUtilsTest.java13
6 files changed, 49 insertions, 25 deletions
diff --git a/security-utils/src/main/java/com/yahoo/security/KeyUtils.java b/security-utils/src/main/java/com/yahoo/security/KeyUtils.java
index 9fe64baa80a..cef0dd9a62e 100644
--- a/security-utils/src/main/java/com/yahoo/security/KeyUtils.java
+++ b/security-utils/src/main/java/com/yahoo/security/KeyUtils.java
@@ -13,6 +13,7 @@ import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
+import org.bouncycastle.math.ec.rfc7748.X25519;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
@@ -317,6 +318,14 @@ public class KeyUtils {
}
}
+ // TODO unify with extractPublicKey()
+ public static XECPublicKey extractX25519PublicKey(XECPrivateKey privateKey) {
+ byte[] privScalar = toRawX25519PrivateKeyBytes(privateKey);
+ byte[] pubPoint = new byte[X25519.POINT_SIZE];
+ X25519.generatePublicKey(privScalar, 0, pubPoint, 0); // scalarMultBase => public key point
+ return fromRawX25519PublicKey(pubPoint);
+ }
+
/**
* Computes a shared secret using the Elliptic Curve Diffie-Hellman (ECDH) protocol for X25519 curves.
* <p>
diff --git a/security-utils/src/main/java/com/yahoo/security/hpke/DHKemX25519HkdfSha256.java b/security-utils/src/main/java/com/yahoo/security/hpke/DHKemX25519HkdfSha256.java
index 430a9d57097..8f6dffcb9c2 100644
--- a/security-utils/src/main/java/com/yahoo/security/hpke/DHKemX25519HkdfSha256.java
+++ b/security-utils/src/main/java/com/yahoo/security/hpke/DHKemX25519HkdfSha256.java
@@ -118,18 +118,13 @@ final class DHKemX25519HkdfSha256 implements Kem {
* shared_secret = ExtractAndExpand(dh, kem_context)
* return shared_secret
* </pre>
- *
- * Implementation note: we take in the key pair to avoid needing to compute the public key (TODO!)
*/
@Override
- public byte[] decap(byte[] enc, KeyPair kpR) {
+ public byte[] decap(byte[] enc, XECPrivateKey skR) {
var pkE = deserializePublicKey(enc);
-
- var skR = (XECPrivateKey)kpR.getPrivate();
- var pkR = (XECPublicKey)kpR.getPublic();
byte[] dh = KeyUtils.ecdh(skR, pkE);
- byte[] pkRm = serializePublicKey(pkR);
+ byte[] pkRm = serializePublicKey(KeyUtils.extractX25519PublicKey(skR));
byte[] kemContext = concat(enc, pkRm);
return extractAndExpand(dh, kemContext);
diff --git a/security-utils/src/main/java/com/yahoo/security/hpke/Hpke.java b/security-utils/src/main/java/com/yahoo/security/hpke/Hpke.java
index e3f233285a8..133798faa99 100644
--- a/security-utils/src/main/java/com/yahoo/security/hpke/Hpke.java
+++ b/security-utils/src/main/java/com/yahoo/security/hpke/Hpke.java
@@ -2,6 +2,7 @@
package com.yahoo.security.hpke;
import java.security.KeyPair;
+import java.security.interfaces.XECPrivateKey;
import java.security.interfaces.XECPublicKey;
import java.util.Arrays;
@@ -246,11 +247,9 @@ public final class Hpke {
* return KeyScheduleR(mode_base, shared_secret, info,
* default_psk, default_psk_id)
* </pre>
- *
- * TODO only take private key, not key pair. Need functionality for X25519 priv -> pub extraction first.
*/
- ContextR setupBaseR(byte[] enc, KeyPair kpR, byte[] info) {
- byte[] sharedSecret = kem.decap(enc, kpR);
+ ContextR setupBaseR(byte[] enc, XECPrivateKey skR, byte[] info) {
+ byte[] sharedSecret = kem.decap(enc, skR);
return new ContextR(keySchedule(MODE_BASE, sharedSecret, info, DEFAULT_PSK, DEFAULT_PSK_ID));
}
@@ -310,8 +309,8 @@ public final class Hpke {
* return pt
* </pre>
*/
- public byte[] openBase(byte[] enc, KeyPair kpR, byte[] info, byte[] aad, byte[] ct) {
- var ctx = setupBaseR(enc, kpR, info);
+ public byte[] openBase(byte[] enc, XECPrivateKey skR, byte[] info, byte[] aad, byte[] ct) {
+ var ctx = setupBaseR(enc, skR, info);
var base = ctx.base;
// TODO wrap any exceptions in OpenError et al?
return aead.open(base.key(), base.nonce(), aad, ct);
diff --git a/security-utils/src/main/java/com/yahoo/security/hpke/Kem.java b/security-utils/src/main/java/com/yahoo/security/hpke/Kem.java
index 7bbb2df0960..99c019a9d0b 100644
--- a/security-utils/src/main/java/com/yahoo/security/hpke/Kem.java
+++ b/security-utils/src/main/java/com/yahoo/security/hpke/Kem.java
@@ -4,6 +4,7 @@ package com.yahoo.security.hpke;
import com.yahoo.security.KeyUtils;
import java.security.KeyPair;
+import java.security.interfaces.XECPrivateKey;
import java.security.interfaces.XECPublicKey;
/**
@@ -30,10 +31,8 @@ public interface Kem {
* "Deterministic algorithm using the private key <code>skR</code> to recover the
* ephemeral symmetric key (the KEM shared secret) from its encapsulated
* representation <code>enc</code>."
- *
- * TODO just take skR instead of entire key pair
*/
- byte[] decap(byte[] enc, KeyPair kpR);
+ byte[] decap(byte[] enc, XECPrivateKey skR);
/** The length in bytes of a KEM shared secret produced by this KEM. */
short nSecret();
diff --git a/security-utils/src/test/java/com/yahoo/security/HpkeTest.java b/security-utils/src/test/java/com/yahoo/security/HpkeTest.java
index 24944759c5c..4455eade7cd 100644
--- a/security-utils/src/test/java/com/yahoo/security/HpkeTest.java
+++ b/security-utils/src/test/java/com/yahoo/security/HpkeTest.java
@@ -9,6 +9,7 @@ import com.yahoo.security.hpke.Kem;
import org.junit.jupiter.api.Test;
import java.security.KeyPair;
+import java.security.interfaces.XECPrivateKey;
import java.security.interfaces.XECPublicKey;
import static com.yahoo.security.ArrayUtils.hex;
@@ -35,6 +36,14 @@ public class HpkeTest {
return new KeyPair(pub, priv);
}
+ private static XECPublicKey pk(KeyPair kp) {
+ return (XECPublicKey) kp.getPublic();
+ }
+
+ private static XECPrivateKey sk(KeyPair kp) {
+ return (XECPrivateKey) kp.getPrivate();
+ }
+
/**
* https://www.rfc-editor.org/rfc/rfc9180.html test vector
*
@@ -55,7 +64,7 @@ public class HpkeTest {
var ciphersuite = Ciphersuite.of(kem, Kdf.hkdfSha256(), Aead.aesGcm128());
var hpke = Hpke.of(ciphersuite);
- var s = hpke.sealBase((XECPublicKey) kpR.getPublic(), info, aad, pt);
+ var s = hpke.sealBase(pk(kpR), info, aad, pt);
// The "enc" output is the ephemeral public key
var expectedEnc = "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431";
@@ -65,7 +74,7 @@ public class HpkeTest {
"6d8770ac83d07bea87e13c512a";
assertEquals(expectedCiphertext, hex(s.ciphertext()));
- byte[] openedPt = hpke.openBase(s.enc(), kpR, info, aad, s.ciphertext());
+ byte[] openedPt = hpke.openBase(s.enc(), sk(kpR), info, aad, s.ciphertext());
assertEquals(hex(pt), hex(openedPt));
}
@@ -78,12 +87,12 @@ public class HpkeTest {
var hpke = Hpke.of(Ciphersuite.defaultSuite());
- var s1 = hpke.sealBase((XECPublicKey) kpR.getPublic(), info, aad, pt);
- byte[] openedPt = hpke.openBase(s1.enc(), kpR, info, aad, s1.ciphertext());
+ var s1 = hpke.sealBase(pk(kpR), info, aad, pt);
+ byte[] openedPt = hpke.openBase(s1.enc(), sk(kpR), info, aad, s1.ciphertext());
assertEquals(hex(pt), hex(openedPt));
- var s2 = hpke.sealBase((XECPublicKey) kpR.getPublic(), info, aad, pt);
- openedPt = hpke.openBase(s2.enc(), kpR, info, aad, s2.ciphertext());
+ var s2 = hpke.sealBase(pk(kpR), info, aad, pt);
+ openedPt = hpke.openBase(s2.enc(), sk(kpR), info, aad, s2.ciphertext());
assertEquals(hex(pt), hex(openedPt));
assertNotEquals(hex(s1.enc()), hex(s2.enc())); // This is the ephemeral public key
@@ -97,13 +106,13 @@ public class HpkeTest {
var kpR = receiverRrfc9180TestVectorKeyPair();
var hpke = Hpke.of(Ciphersuite.defaultSuite());
- var s = hpke.sealBase((XECPublicKey) kpR.getPublic(), info, aad, pt);
+ var s = hpke.sealBase(pk(kpR), info, aad, pt);
byte[] badInfo = toUtf8Bytes("lesser info");
// TODO better exception classes! Triggers AEAD auth tag mismatch behind the scenes
- assertThrows(RuntimeException.class, () -> hpke.openBase(s.enc(), kpR, badInfo, aad, s.ciphertext()));
+ assertThrows(RuntimeException.class, () -> hpke.openBase(s.enc(), sk(kpR), badInfo, aad, s.ciphertext()));
byte[] badAad = toUtf8Bytes("non-groovy AAD");
- assertThrows(RuntimeException.class, () -> hpke.openBase(s.enc(), kpR, info, badAad, s.ciphertext()));
+ assertThrows(RuntimeException.class, () -> hpke.openBase(s.enc(), sk(kpR), info, badAad, s.ciphertext()));
}
}
diff --git a/security-utils/src/test/java/com/yahoo/security/KeyUtilsTest.java b/security-utils/src/test/java/com/yahoo/security/KeyUtilsTest.java
index fbf27b67f4b..97fd8ce9e41 100644
--- a/security-utils/src/test/java/com/yahoo/security/KeyUtilsTest.java
+++ b/security-utils/src/test/java/com/yahoo/security/KeyUtilsTest.java
@@ -170,4 +170,17 @@ public class KeyUtilsTest {
assertEquals(pubHex, xecHexFromPub(pub2));
}
+ @Test
+ void can_extract_public_key_from_x25519_private_key() {
+ var priv = xecPrivFromHex("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
+ var expectedPubHex = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
+ var pub = KeyUtils.extractX25519PublicKey(priv);
+ assertEquals(expectedPubHex, xecHexFromPub(pub));
+
+ priv = xecPrivFromHex("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
+ expectedPubHex = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f";
+ pub = KeyUtils.extractX25519PublicKey(priv);
+ assertEquals(expectedPubHex, xecHexFromPub(pub));
+ }
+
}