Skip to content

Report malformed JSON keyset fields as IOException instead of an uncaught exception#70

Open
adilburaksen wants to merge 1 commit into
tink-crypto:mainfrom
adilburaksen:fix/jsonkeysetreader-malformed-field-exception
Open

Report malformed JSON keyset fields as IOException instead of an uncaught exception#70
adilburaksen wants to merge 1 commit into
tink-crypto:mainfrom
adilburaksen:fix/jsonkeysetreader-malformed-field-exception

Conversation

@adilburaksen

Copy link
Copy Markdown

Problem

JsonKeysetReader.read() and readEncrypted() declare throws IOException, and the public TinkJsonProtoKeysetFormat.parseKeyset* helpers wrap that into throws GeneralSecurityException. Their try blocks catch JsonParseException | IllegalStateException and rethrow as IOException.

However, when a JSON keyset sets a string-typed field — e.g. status, typeUrl, keyMaterialType, value, encryptedKeyset — to a JSON object or array, gson's JsonElement.getAsString() throws UnsupportedOperationException. That is a RuntimeException and is not covered by the existing catch, so it propagates uncaught, past the documented IOException / GeneralSecurityException contract.

A caller that parses an untrusted keyset (for example a public keyset via parseKeysetWithoutSecret) and handles only the declared exceptions will therefore see an unexpected UnsupportedOperationException instead of a clean parse error.

Minimal reproducer (the field is a {} instead of a string):

{"primaryKeyId":1,"key":[{"keyData":{"typeUrl":{},"value":"","keyMaterialType":"SYMMETRIC"},"status":"ENABLED","keyId":1,"outputPrefixType":"TINK"}]}

getAsString() on the object-valued typeUrl throws UnsupportedOperationException. (The existing testRead_outputPrefixTypeIsNotAString test uses a number, which gson coerces to a string, so the object/array case is not currently exercised.)

Fix

Add UnsupportedOperationException to the catch clauses in read() and readEncrypted() so malformed input is reported as IOException, consistent with the other parse errors.

} catch (JsonParseException | IllegalStateException | UnsupportedOperationException e) {
  throw new IOException(e);
}

Happy to add a test covering object/array-valued string fields if preferred.

…ught exception

JsonKeysetReader.read()/readEncrypted() declare `throws IOException`, and the
public TinkJsonProtoKeysetFormat.parseKeyset* helpers wrap that into
`throws GeneralSecurityException`. When a JSON keyset sets a string-typed field
(e.g. "status", "typeUrl", "keyMaterialType", "value", "encryptedKeyset") to a
JSON object or array, gson's JsonElement.getAsString() throws
UnsupportedOperationException, a RuntimeException not covered by the existing
`catch (JsonParseException | IllegalStateException)`. It then propagates
uncaught, past the documented IOException / GeneralSecurityException contract,
so a caller parsing an untrusted (e.g. public) keyset that only handles the
declared exceptions crashes.

Add UnsupportedOperationException to the catch clauses so malformed input is
reported as IOException, consistent with other parse errors.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant