Skip to content

Commit

Permalink
update openapi docs for several endpoints, notably those with PQXDH c…
Browse files Browse the repository at this point in the history
…hanges

Co-authored-by: Katherine Yen <[email protected]>
  • Loading branch information
jkt-signal and katherine-signal committed Jul 6, 2023
1 parent 098b177 commit e5f4c17
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public AccountControllerV2(final AccountsManager accountsManager, final ChangeNu
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Change number", description = "Changes a phone number for an existing account.")
@ApiResponse(responseCode = "200", description = "The phone number associated with the authenticated account was changed successfully", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "403", description = "Verification failed for the provided Registration Recovery Password")
@ApiResponse(responseCode = "409", description = "Mismatched number of devices or device ids in 'devices to notify' list", content = @Content(schema = @Schema(implementation = MismatchedDevices.class)))
@ApiResponse(responseCode = "410", description = "Mismatched registration ids in 'devices to notify' list", content = @Content(schema = @Schema(implementation = StaleDevices.class)))
Expand Down Expand Up @@ -159,7 +160,17 @@ public AccountIdentityResponse changeNumber(@Auth final AuthenticatedAccount aut
@Path("/phone_number_identity_key_distribution")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Updates key material for the phone-number identity for all devices and sends a synchronization message to companion devices")
@Operation(summary = "Set phone-number identity keys",
description = "Updates key material for the phone-number identity for all devices and sends a synchronization message to companion devices")
@ApiResponse(responseCode = "200", description = "Indicates the transaction was successful and returns basic information about this account.", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "403", description = "This endpoint can only be invoked from the account's primary device.")
@ApiResponse(responseCode = "422", description = "The request body failed validation.")
@ApiResponse(responseCode = "425", description = "Not all of this account's devices support phone-number identities yet.")
@ApiResponse(responseCode = "409", description = "The set of devices specified in the request does not match the set of devices active on the account.",
content = @Content(schema = @Schema(implementation = MismatchedDevices.class)))
@ApiResponse(responseCode = "410", description = "The registration IDs provided for some devices do not match those stored on the server.",
content = @Content(schema = @Schema(implementation = StaleDevices.class)))
public AccountIdentityResponse distributePhoneNumberIdentityKeys(@Auth final AuthenticatedAccount authenticatedAccount,
@NotNull @Valid final PhoneNumberIdentityKeyDistributionRequest request) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
import io.micrometer.core.instrument.Tags;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.time.Duration;
import java.time.Instant;
Expand Down Expand Up @@ -86,7 +88,10 @@ public KeysController(RateLimiters rateLimiters, KeysManager keys, AccountsManag

@GET
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Returns the number of available one-time prekeys for this device")
@Operation(summary = "Get prekey count",
description = "Gets the number of one-time prekeys uploaded for this device and still available")
@ApiResponse(responseCode = "200", description = "Body contains the number of available one-time prekeys for the device.", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
public PreKeyCount getStatus(@Auth final AuthenticatedAccount auth,
@QueryParam("identity") final Optional<String> identityType) {

Expand All @@ -101,7 +106,15 @@ public PreKeyCount getStatus(@Auth final AuthenticatedAccount auth,
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@ChangesDeviceEnabledState
@Operation(summary = "Sets the identity key for the account or phone-number identity and/or prekeys for this device")
@Operation(summary = "Upload new prekeys",
description = """
Upload new prekeys for this device. Can also be used, from the primary device only, to set the account's identity
key, but this is deprecated now that accounts can be created atomically.
""")
@ApiResponse(responseCode = "200", description = "Indicates that new keys were successfully stored.")
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "403", description = "Attempt to change identity key from a non-primary device.")
@ApiResponse(responseCode = "422", description = "Invalid request format.")
public void setKeys(@Auth final DisabledPermittedAuthenticatedAccount disabledPermittedAuth,
@RequestBody @NotNull @Valid final PreKeyState preKeys,

Expand Down Expand Up @@ -176,8 +189,15 @@ public void setKeys(@Auth final DisabledPermittedAuthenticatedAccount disabledPe
@GET
@Path("/{identifier}/{device_id}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Retrieves the public identity key and available device prekeys for a specified account or phone-number identity")
public Response getDeviceKeys(@Auth Optional<AuthenticatedAccount> auth,
@Operation(summary = "Fetch public keys for another user",
description = "Retrieves the public identity key and available device prekeys for a specified account or phone-number identity")
@ApiResponse(responseCode = "200", description = "Indicates at least one prekey was available for at least one requested device.", useReturnTypeSchema = true)
@ApiResponse(responseCode = "401", description = "Account authentication check failed and unidentified-access key was not supplied or invalid.")
@ApiResponse(responseCode = "404", description = "Requested identity or device does not exist, is not active, or has no available prekeys.")
@ApiResponse(responseCode = "429", description = "Rate limit exceeded.", headers = @Header(
name = "Retry-After",
description = "If present, a positive integer indicating the number of seconds before a subsequent attempt could succeed"))
public PreKeyResponse getDeviceKeys(@Auth Optional<AuthenticatedAccount> auth,
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,

@Parameter(description="the account or phone-number identifier to retrieve keys for")
Expand Down Expand Up @@ -241,16 +261,23 @@ public Response getDeviceKeys(@Auth Optional<AuthenticatedAccount> auth,
final IdentityKey identityKey = usePhoneNumberIdentity ? target.getPhoneNumberIdentityKey() : target.getIdentityKey();

if (responseItems.isEmpty()) {
return Response.status(404).build();
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return Response.ok().entity(new PreKeyResponse(identityKey, responseItems)).build();
return new PreKeyResponse(identityKey, responseItems);
}

@Timed
@PUT
@Path("/signed")
@Consumes(MediaType.APPLICATION_JSON)
@ChangesDeviceEnabledState
@Operation(summary = "Upload a new signed prekey",
description = """
Upload a new signed elliptic-curve prekey for this device. Deprecated; use PUT /v2/keys with instead.
""")
@ApiResponse(responseCode = "200", description = "Indicates that new prekey was successfully stored.")
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "422", description = "Invalid request format.")
public void setSignedKey(@Auth final AuthenticatedAccount auth,
@Valid final ECSignedPreKey signedPreKey,
@QueryParam("identity") final Optional<String> identityType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -27,8 +28,8 @@ public record ChangeNumberRequest(
Must not be combined with `recoveryPassword`.""")
String sessionId,

@Schema(description="""
The recovery password for the new phone number, if using a recovery password to authenticate this request.
@Schema(type="string", description="""
The base64-encoded recovery password for the new phone number, if using a recovery password to authenticate this request.
Must not be combined with `sessionId`.""")
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) byte[] recoveryPassword,

Expand All @@ -43,10 +44,11 @@ public record ChangeNumberRequest(
@JsonDeserialize(using = IdentityKeyAdapter.Deserializer.class)
@NotNull IdentityKey pniIdentityKey,

@Schema(description="""
@ArraySchema(
arraySchema=@Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keysManager
associated with the new identity key and their new prekeys.
Exactly one message must be supplied for each enabled device other than the sending (primary) device.""")
Exactly one message must be supplied for each enabled device other than the sending (primary) device."""))
@NotNull @Valid List<@NotNull @Valid IncomingMessage> deviceMessages,

@Schema(description="""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,23 @@

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.whispersystems.textsecuregcm.util.ECPublicKeyAdapter;

public record ECPreKey(long keyId,
@JsonSerialize(using = ECPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = ECPublicKeyAdapter.Deserializer.class)
ECPublicKey publicKey) implements PreKey<ECPublicKey> {
public record ECPreKey(
@Schema(description="""
An arbitrary ID for this key, which will be provided by peers using this key to encrypt messages so the private key can be looked up.
Should not be zero. Should be less than 2^24.
""")
long keyId,

@JsonSerialize(using = ECPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = ECPublicKeyAdapter.Deserializer.class)
@Schema(type="string", description="""
The public key, serialized in libsignal's elliptic-curve public key format and then base64-encoded.
""")
ECPublicKey publicKey) implements PreKey<ECPublicKey> {

@Override
public byte[] serializedPublicKey() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,33 @@

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import org.whispersystems.textsecuregcm.util.ECPublicKeyAdapter;
import java.util.Arrays;
import java.util.Objects;

public record ECSignedPreKey(long keyId,

@JsonSerialize(using = ECPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = ECPublicKeyAdapter.Deserializer.class)
ECPublicKey publicKey,

@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
byte[] signature) implements SignedPreKey<ECPublicKey> {
public record ECSignedPreKey(
@Schema(description="""
An arbitrary ID for this key, which will be provided by peers using this key to encrypt messages so the private key can be looked up.
Should not be zero. Should be less than 2^24.
""")
long keyId,

@JsonSerialize(using = ECPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = ECPublicKeyAdapter.Deserializer.class)
@Schema(type="string", description="""
The public key, serialized in libsignal's elliptic-curve public key format and then base64-encoded.
""")
ECPublicKey publicKey,

@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@Schema(type="string", description="""
The signature of the serialized `publicKey` with the account (or phone-number identity)'s identity key, base64-encoded.
""")
byte[] signature) implements SignedPreKey<ECPublicKey> {

@Override
public byte[] serializedPublicKey() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,34 @@

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import org.signal.libsignal.protocol.kem.KEMPublicKey;
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
import org.whispersystems.textsecuregcm.util.KEMPublicKeyAdapter;
import java.util.Arrays;
import java.util.Objects;

public record KEMSignedPreKey(long keyId,

@JsonSerialize(using = KEMPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = KEMPublicKeyAdapter.Deserializer.class)
KEMPublicKey publicKey,

@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
byte[] signature) implements SignedPreKey<KEMPublicKey> {
public record KEMSignedPreKey(
@Schema(description="""
An arbitrary ID for this key, which will be provided by peers using this key to encrypt messages so the private key can be looked up.
Should not be zero. Should be less than 2^24. The owner of this key must be able to determine from the key ID whether this represents
a single-use or last-resort key, but another party should *not* be able to tell.
""")
long keyId,

@JsonSerialize(using = KEMPublicKeyAdapter.Serializer.class)
@JsonDeserialize(using = KEMPublicKeyAdapter.Deserializer.class)
@Schema(type="string", description="""
The public key, serialized in libsignal's Kyber1024 public key format and then base64-encoded.
""")
KEMPublicKey publicKey,

@JsonSerialize(using = ByteArrayAdapter.Serializing.class)
@JsonDeserialize(using = ByteArrayAdapter.Deserializing.class)
@Schema(type="string", description="""
The signature of the serialized `publicKey` with the account (or phone-number identity)'s identity key, base64-encoded.
""")
byte[] signature) implements SignedPreKey<KEMPublicKey> {

@Override
public byte[] serializedPublicKey() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package org.whispersystems.textsecuregcm.entities;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -24,10 +25,12 @@ public record PhoneNumberIdentityKeyDistributionRequest(

@NotNull
@Valid
@Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keysManager
associated with the new identity key and their new prekeys.
Exactly one message must be supplied for each enabled device other than the sending (primary) device.""")
@ArraySchema(
arraySchema=@Schema(description="""
A list of synchronization messages to send to companion devices to supply the private keys
associated with the new identity key and their new prekeys.
Exactly one message must be supplied for each enabled device other than the sending (primary) device.
"""))
List<@NotNull @Valid IncomingMessage> deviceMessages,

@NotNull
Expand All @@ -47,7 +50,7 @@ Exactly one message must be supplied for each enabled device other than the send

@NotNull
@Valid
@Schema(description="The new registration ID to use for the phone-number identity of each device")
@Schema(description="The new registration ID to use for the phone-number identity of each device, including this one.")
Map<Long, Integer> pniRegistrationIds) {

@AssertTrue
Expand Down

0 comments on commit e5f4c17

Please sign in to comment.