Skip to content

Commit

Permalink
Implement support for temporary keys (#632)
Browse files Browse the repository at this point in the history
* Add initial implementation of temporary keys

* Add new version 3.3

* Add optional temporary key ID to signature header

* Fix default constructors

* Move temporary key ID to request body

* Added ECIES V3.3 test vectors generated by iOS SDK

* Empty lines cleanup

* Added encrypt-decrypt tests for ECIES v3.3

* Simplify check for temporary key presence in 3.3 protocol

---------

Co-authored-by: Juraj Ďurech <durech.juraj@gmail.com>
Co-authored-by: Juraj Ďurech <1719814+hvge@users.noreply.github.com>
  • Loading branch information
3 people committed Aug 27, 2024
1 parent fffca2c commit 7532ada
Show file tree
Hide file tree
Showing 12 changed files with 511 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public ClientEncryptor getClientEncryptor(EncryptorId encryptorId, EncryptorPara
validateParameters(encryptorId, encryptorParameters);
final ClientEncryptor encryptor;
switch (encryptorParameters.getProtocolVersion()) {
case "3.2", "3.1", "3.0" -> {
case "3.3", "3.2", "3.1", "3.0" -> {
encryptor = new ClientEciesEncryptor(encryptorId, encryptorParameters);
}
default -> {
Expand Down Expand Up @@ -106,7 +106,7 @@ public ServerEncryptor getServerEncryptor(EncryptorId encryptorId, EncryptorPara
validateParameters(encryptorId, encryptorParameters);
final ServerEncryptor encryptor;
switch (encryptorParameters.getProtocolVersion()) {
case "3.2", "3.1", "3.0" -> {
case "3.3", "3.2", "3.1", "3.0" -> {
encryptor = new ServerEciesEncryptor(encryptorId, encryptorParameters);
}
default -> {
Expand Down Expand Up @@ -154,7 +154,7 @@ public RequestResponseValidator getRequestResponseValidator(String protocolVersi
throw new EncryptorException("Missing protocolVersion parameter");
}
switch (protocolVersion) {
case "3.2", "3.1", "3.0" -> {
case "3.3", "3.2", "3.1", "3.0" -> {
return new EciesRequestResponseValidator(protocolVersion);
}
default -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ public ClientEciesEncryptor(EncryptorId encryptorId, EncryptorParameters paramet
encryptorId.scope(),
parameters.getProtocolVersion(),
parameters.getApplicationKey(),
parameters.getActivationIdentifier()
parameters.getActivationIdentifier(),
parameters.getTemporaryKeyId()
);
}

Expand Down Expand Up @@ -156,6 +157,7 @@ public EncryptedRequest encryptRequest(byte[] data) throws EncryptorException {
}

return new EncryptedRequest(
encryptorParameters.getTemporaryKeyId(),
base64Encoder.encodeToString(eciesCryptogram.getEphemeralPublicKey()),
base64Encoder.encodeToString(eciesCryptogram.getEncryptedData()),
base64Encoder.encodeToString(eciesCryptogram.getMac()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class EciesRequestResponseValidator implements RequestResponseValidator {
/**
* Protocol versions supported in this validator.
*/
private final static Set<String> supportedVersions = Set.of("3.2", "3.1", "3.0");
private final static Set<String> supportedVersions = Set.of("3.3", "3.2", "3.1", "3.0");

/**
* Indicate that request and response must contain timestamp and nonce. This is valid for protocol V3.2+.
Expand All @@ -53,8 +53,8 @@ public EciesRequestResponseValidator(String protocolVersion) throws EncryptorExc
if (!supportedVersions.contains(protocolVersion)) {
throw new EncryptorException("Unsupported protocol version " + protocolVersion);
}
this.useTimestamp = "3.2".equals(protocolVersion);
this.useNonceForRequest = "3.2".equals(protocolVersion) || "3.1".equals(protocolVersion);
this.useTimestamp = "3.3".equals(protocolVersion) || "3.2".equals(protocolVersion);
this.useNonceForRequest = "3.3".equals(protocolVersion) || "3.2".equals(protocolVersion) || "3.1".equals(protocolVersion);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ public ServerEciesEncryptor(EncryptorId encryptorId, EncryptorParameters paramet
encryptorId.scope(),
parameters.getProtocolVersion(),
parameters.getApplicationKey(),
parameters.getActivationIdentifier()
parameters.getActivationIdentifier(),
parameters.getTemporaryKeyId()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
@Data
@AllArgsConstructor
public class EncryptedRequest {
private String temporaryKeyId;
private String ephemeralPublicKey;
private String encryptedData;
private String mac;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ public class EncryptorParameters {
private String protocolVersion;
private String applicationKey;
private String activationIdentifier;
private String temporaryKeyId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,27 +62,47 @@ public static long generateTimestamp() {
* @param protocolVersion Protocol version.
* @param applicationKey Application key.
* @param activationId Activation ID.
* @param temporaryKeyId Temporary key ID.
* @return Derived associated data.
* @throws EciesException In case that activation ID is required but is missing.
*/
public static byte[] deriveAssociatedData(EncryptorScope scope, String protocolVersion, String applicationKey, String activationId) throws EciesException {
public static byte[] deriveAssociatedData(EncryptorScope scope, String protocolVersion, String applicationKey, String activationId, String temporaryKeyId) throws EciesException {
if (protocolVersion == null) {
throw new EciesException("Protocol version is missing");
}
if ("3.2".equals(protocolVersion)) {
if (applicationKey == null) {
throw new EciesException("Application key is missing");
switch (protocolVersion) {
case "3.2": {
if (applicationKey == null) {
throw new EciesException("Application key is missing");
}
if (scope == EncryptorScope.ACTIVATION_SCOPE) {
if (activationId == null) {
throw new EciesException("Activation ID is missing in ACTIVATION_SCOPE");
}
return ByteUtils.concatStrings(protocolVersion, applicationKey, activationId);
} else {
return ByteUtils.concatStrings(protocolVersion, applicationKey);
}
}
if (scope == EncryptorScope.ACTIVATION_SCOPE) {
if (activationId == null) {
throw new EciesException("Activation ID is missing in ACTIVATION_SCOPE");
case "3.3": {
if (applicationKey == null) {
throw new EciesException("Application key is missing");
}
if (temporaryKeyId == null) {
throw new EciesException("Missing temporary key identifier");
}
if (scope == EncryptorScope.ACTIVATION_SCOPE) {
if (activationId == null) {
throw new EciesException("Activation ID is missing in ACTIVATION_SCOPE");
}
return ByteUtils.concatStrings(protocolVersion, applicationKey, activationId, temporaryKeyId);
} else {
return ByteUtils.concatStrings(protocolVersion, applicationKey, temporaryKeyId);
}
return ByteUtils.concatStrings(protocolVersion, applicationKey, activationId);
} else {
return ByteUtils.concatStrings(protocolVersion, applicationKey);
}
} else {
return null;
default: {
return null;
}
}
}

Expand Down Expand Up @@ -130,24 +150,28 @@ public static byte[] deriveSharedInfo2(String protocolVersion, byte[] sharedInfo
if (sharedInfo2Base == null) {
throw new EciesException("Missing sharedInfo2Base parameter");
}
if ("3.2".equals(protocolVersion)) {
if (nonce == null) {
throw new EciesException("Missing nonce parameter");
}
if (timestamp == null) {
throw new EciesException("Missing timestamp parameter");
switch (protocolVersion) {
case "3.3", "3.2": {
if (nonce == null) {
throw new EciesException("Missing nonce parameter");
}
if (timestamp == null) {
throw new EciesException("Missing timestamp parameter");
}
if (associatedData == null) {
throw new EciesException("Missing associatedData parameter");
}
return ByteUtils.concatWithSizes(
sharedInfo2Base,
nonce,
ByteBuffer.allocate(Long.BYTES).putLong(timestamp).array(),
ephemeralPublicKey,
associatedData);
}
if (associatedData == null) {
throw new EciesException("Missing associatedData parameter");
default: {
return sharedInfo2Base;
}
return ByteUtils.concatWithSizes(
sharedInfo2Base,
nonce,
ByteBuffer.allocate(Long.BYTES).putLong(timestamp).array(),
ephemeralPublicKey,
associatedData);
}
return sharedInfo2Base;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public byte[] computeTokenDigest(byte[] nonce, byte[] timestamp, String version,
final byte[] amp = "&".getBytes(StandardCharsets.UTF_8);
final byte[] data;
switch (version) {
case "3.2" -> data = ByteUtils.concat(nonce, amp, timestamp, amp, version.getBytes(StandardCharsets.UTF_8));
case "3.3", "3.2" -> data = ByteUtils.concat(nonce, amp, timestamp, amp, version.getBytes(StandardCharsets.UTF_8));
case "3.0", "3.1" -> data = ByteUtils.concat(nonce, amp, timestamp);
default -> throw new GenericCryptoException("Unsupported version value was specified: " + version);
}
Expand Down
Loading

0 comments on commit 7532ada

Please sign in to comment.