{"record":{"id":"8925b05eee279959","repo":"signalapp/Signal-Server","slug":"access-key-length-must-be-16","errorCode":null,"errorMessage":"access key length must be 16","messagePattern":"access key length must be 16","errorType":"http","errorClass":"WebApplicationException","httpStatus":401,"severity":"error","filePath":"service/src/main/java/org/whispersystems/textsecuregcm/auth/Anonymous.java","lineNumber":20,"sourceCode":" * Copyright 2013-2020 Signal Messenger, LLC\n * SPDX-License-Identifier: AGPL-3.0-only\n */\n\npackage org.whispersystems.textsecuregcm.auth;\n\nimport jakarta.ws.rs.WebApplicationException;\nimport jakarta.ws.rs.core.Response;\nimport java.util.Base64;\n\npublic class Anonymous {\n\n  private final byte[] unidentifiedSenderAccessKey;\n\n  public Anonymous(String header) {\n    try {\n      this.unidentifiedSenderAccessKey = Base64.getDecoder().decode(header);\n      if (unidentifiedSenderAccessKey.length != UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH) {\n        throw new WebApplicationException(\"access key length must be 16\", Response.Status.UNAUTHORIZED);\n      }\n    } catch (IllegalArgumentException e) {\n      throw new WebApplicationException(e, Response.Status.UNAUTHORIZED);\n    }\n  }\n\n  public byte[] getAccessKey() {\n    return unidentifiedSenderAccessKey;\n  }\n}\n","sourceCodeStart":2,"sourceCodeEnd":31,"githubUrl":"https://github.com/signalapp/Signal-Server/blob/100ab61c82627582c867d19e1c0561ba2781e927/service/src/main/java/org/whispersystems/textsecuregcm/auth/Anonymous.java#L2-L31","documentation":"Anonymous is a JAX-RS auth provider wrapper for unidentified-delivery access keys. It Base64-decodes the X-Signal-Access-Key header and rejects it with HTTP 401 unless the decoded byte array is exactly UnidentifiedAccessUtil.UNIDENTIFIED_ACCESS_KEY_LENGTH (16) bytes. This prevents calls to profiles/messages from using malformed access keys.","triggerScenarios":"Sending a request with an access-key header whose Base64 value decodes to a byte array that is not 16 bytes long (e.g. 0 bytes, 8, 32 bytes).","commonSituations":"Clients building the header from a raw string instead of the account's 16-byte unidentified access key; double/truncated Base64 encoding; sending an empty header value that Base64-decodes to an empty array; old clients after a key-length change.","solutions":["Ensure the header is Base64 of the account's 16-byte unidentified access key (UUID-derived, per Signal protocol)","Decode your Base64 client-side and assert the array length is 16 before sending","Regenerate the access key from the correct account identity key material if it is stale or misderived"],"exampleFix":"// before\nString header = Base64.getEncoder().encodeToString(uuid.toString().getBytes());\n// after\nbyte[] key = UnidentifiedAccessUtil.getAccessKeyFor(acdUuid, identityKey);\nString header = Base64.getEncoder().encodeToString(key); // 16 bytes","handlingStrategy":"validation","validationCode":"byte[] key = Base64.getDecoder().decode(header);\nif (key.length != 16) throw new IllegalArgumentException(\"access key must be 16 bytes, got \" + key.length);","typeGuard":null,"tryCatchPattern":"try { authFilter.call(); } catch (WebApplicationException e) { if (e.getResponse().getStatus() == 401) { /* regenerate access key and retry once */ } }","preventionTips":["Derive the access key with the standard 16-byte HMAC function from the identity key","Unit-test that your encoded header decodes to exactly 16 bytes","Never hand-roll the key derivation; use the client library"],"tags":["http-401","base64","access-key","auth"],"backgroundTag":"invalid-argument-value","analyzedSha":"100ab61c82627582c867d19e1c0561ba2781e927","analyzedAt":"2026-09-09T13:29:47.883Z","contentChangedAt":"2026-09-09T13:29:47.883Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}