summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2023-02-08 11:05:46 -0600
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-14 21:11:29 +0000
commit28bb49c88d432b60f4b6d9a747886782981fda89 (patch)
tree74ba37a19ce5fb7c807a1f3645ad4ed6a0742b79
parent9355b6a325e2606e41198caa07ded85e68a5b353 (diff)
downloadmongo-28bb49c88d432b60f4b6d9a747886782981fda89.tar.gz
SERVER-56031 Architecture guide for JWT crypto
-rw-r--r--src/mongo/crypto/README.JWT.md184
-rw-r--r--src/mongo/crypto/README.md3
2 files changed, 187 insertions, 0 deletions
diff --git a/src/mongo/crypto/README.JWT.md b/src/mongo/crypto/README.JWT.md
new file mode 100644
index 00000000000..c447af6ef32
--- /dev/null
+++ b/src/mongo/crypto/README.JWT.md
@@ -0,0 +1,184 @@
+# JSON Web Token Library
+
+At present, usage of JWS in MongoDB is limited to the Linux platform only and is not implemented on other platforms.
+Since signature validation is not available on other platforms, use of the unvalidated JWT types, while present, is not useful.
+
+* [Glossary](#glossary)
+* [`JWKManager`](#jwkmanager)
+* [`JWSValidator`](#jwsvalidator)
+* [`JWSValidatedToken`](#jwsvalidatedtoken)
+* [Compact Serialization Format](#compact-serialization-format)
+
+## Glossary
+
+* **JWK** (JSON Web Key): A human readable representation of a cryptographic key.
+ * See [RFC 7517](https://www.rfc-editor.org/rfc/rfc7517) JSON Web Key
+ * Note: This library currently supports [RSA](https://www.rfc-editor.org/rfc/rfc7517#section-9.3) based keys only.
+* **JWS** (JSON Web Signature): A cryptographic signature on a JWT, typically presented as a single object with the token and a header.
+ * See [RFC 7515](https://www.rfc-editor.org/rfc/rfc7515) JSON Web Signature
+ * Note: This library currently supports the [Compact Serialization](https://www.rfc-editor.org/rfc/rfc7515#section-3.1) only.
+* **JWT** (JSON Web Token): A JSON object representing a number of claims such as, but not limited to: bearer identity, issuer, and validity.
+ * See [RFC 7519](https://www.rfc-editor.org/rfc/rfc7519) JSON Web Token
+
+## JWKManager
+
+[JWKManager](https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/jwk_manager.h)
+loads `JWKSet`s from an HTTPS endpoint, parses the received JSON using the
+`JWKSet`, `JWK`, and `JWKRSA` types from IDL, and instantiates [`JWSValidator`s](#jwsvalidator) with the key material.
+
+Later, when validating a client supplied token, the application will use the
+[`JWSValidatedToken`](#jwsvalidatedtoken) type with a `JWKManager` to validate the token.
+
+### JSON Web Keys
+
+* `JWK`: The base key material type in IDL. This parses only the `kid` (Key ID) and `kty` (Key Type) fields.
+ In order to expect and process key specific data, the `kty` must already be known, therefore
+ type specific IDL structs are defined as chaining the base `JWK` type.
+ * `JWKRSA`: Chains the base `JWK` type and adds expected fields `n` and `e` which represent the
+ modulus and public-exponent portions of the RSA key respectively.
+* `JWKSet`: A simple wrapper struct containing a single field named `keys` of type `array<object>`.
+ This allows the [`JWKManager`](#jwkmanager) class to load a `JWKSet` URI resource and pull out a set of keys
+ which are expected to conform to the `JWK` interface, and as of this writing, represent `JWKRSA` data specifically.
+
+These types, as well as [`JWSHeader`](#jwsheader) and [`JWT`](#jwt) can be found in [jwt\_types.idl](jwt_types.idl).
+
+### Example JWK file
+
+A typical JSON file containing keys may look something like the following:
+
+```json
+{
+ "keys": [
+ {
+ "kid": "custom-key-1",
+ "kty": "RSA",
+ "n": "ALtUlNS31SzxwqMzMR9jKOJYDhHj8zZtLUYHi3s1en3wLdILp1Uy8O6Jy0Z66tPyM1u8lke0JK5gS-40yhJ-bvqioW8CnwbLSLPmzGNmZKdfIJ08Si8aEtrRXMxpDyz4Is7JLnpjIIUZ4lmqC3MnoZHd6qhhJb1v1Qy-QGlk4NJy1ZI0aPc_uNEUM7lWhPAJABZsWc6MN8flSWCnY8pJCdIk_cAktA0U17tuvVduuFX_94763nWYikZIMJS_cTQMMVxYNMf1xcNNOVFlUSJHYHClk46QT9nT8FWeFlgvvWhlXfhsp9aNAi3pX-KxIxqF2wABIAKnhlMa3CJW41323Js",
+ "e": "AQAB"
+ },
+ {
+ "kid": "custom-key-2",
+ "kty": "RSA",
+ "n": "ANBv7-YFoyL8EQVhig7yF8YJogUTW-qEkE81s_bs2CTsI1oepDFNAeMJ-Krfx1B7yllYAYtScZGo_l60R9Ou4X89LA66bnVRWVFCp1YV1r0UWtn5hJLlAbqKseSmjdwZlL_e420GlUAiyYsiIr6wltC1dFNYyykq62RhfYhM0xpnt0HiN-k71y9A0GO8H-dFU1WgOvEYMvHmDAZtAP6RTkALE3AXlIHNb4mkOc9gwwn-7cGBc08rufYcniKtS0ZHOtD1aE2CTi1MMQMKkqtVxWIdTI3wLJl1t966f9rBHR6qVtTV8Qpq1bquUc2oaHjR4lPTf0Z_hTaELJa5-BBbvJU",
+ "e": "AQAB"
+ }
+ ]
+}
+```
+
+## JWSValidator
+
+The [`JWSValidator`](https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/jws_validator.h)
+interface provides a platform agnostic API for verifying the signatures on `JWS` signed `JWT` payloads.
+Each instance of a `JWSValidator` is created (via `JWSValidator::create`) with a specific key,
+and may have it's `JWSValidator->validate()` invoked multiple times (concurrently) to validate tokens
+as they are received from third parties such as connected clients.
+
+Platform specific implementations of the cryptographic functions may be found in:
+
+* Linux: [jws\_validator\_openssl.cpp](https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/jws_validator_openssl.cpp)
+* Windows: [jws\_validator\_windows.cpp](https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/jws_validator_windows.cpp) UNIMPLEMENTED
+* macOS: [jws\_validator\_apple.cpp](https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/jws_validator_apple.cpp) UNIMPLEMENTED
+* Non-TLS builds: [jws\_validator\_none.cpp](https://github.com/mongodb/mongo/blob/master/src/mongo/crypto/jws_validator_none.cpp) UNIMPLEMENTED
+
+## JWSValidatedToken
+
+A token **MUST NOT** be trusted from any third party unless its contents have been duly validated.
+The `JWSValidatedToken` exists to encapsulate the processing of a signed token into usable fields while
+maintaining a type on the post-processed token as well.
+
+An application will construct a `JWSValidatedToken` by passing both a
+signed [`JWS Compact Serialization`](#compact-serialization-format) and a [`JWKManager`](#jwkmanager).
+1. The token's header is parsed using IDL type [`JWSHeader`](#jwsheader) to determine the `kid` (Key ID) which was used for signing.
+2. The [`JWKManager`](#jwkmanager) is queried for a suitable [`JWSValidator`](#jwsvalidator).
+ 1. If the requested `kid` is unknown to the [`JWKManager`](#jwkmanager), it will requery its `JWKSet` URI to reload from the key server.
+3. That validator is used to check the provided signature against the header and body payload.
+4. The body of the token is parsed using IDL type [`JWT`](#jwt).
+5. Relevant validity claims `nbf` ([Not before](https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5)) and `exp` ([Expires At](https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4)) are verified.
+
+If, at any point during this construction, an error is encountered, a `DBException` will be thrown and no `JWSValidatedToken` will be created.
+
+Callers **MUST** validate `iss` (Issuer) and `aud` (Audience) independently.
+
+Callers **SHOULD** only retain `JWSValidatedToken` objects.
+To access fields, use the `getBody()`/`getBodyBSON()` accessors on an as-needed basis
+from the `JWSValidatedToken` object rather than storing these values by themselves.
+This ensures that the data being used has been validated.
+
+**Directly parsing the signed JWS compact serialization using *any* other means
+than `JWSValidatedToken` should be considered an error and rejected during code review.**
+
+## Compact Serialization Format
+
+A typical Compact Serialization string, as received from a third party, may look something like the following:
+
+```
+eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImN1c3RvbS1rZXktMSJ9.
+eyJpc3MiOiJodHRwczovL3Rlc3Qua2VybmVsLm1vbmdvZGIuY29tL29pZGMvaXNzdWVyMSIsInN1YiI6InVzZXIxQG1vbmdvZGIuY29tIiwibmJmIjoxNjYxMzc0MDc3LCJleHAiOjIxNDc0ODM2NDcsImF1ZCI6WyJqd3RAa2VybmVsLm1vbmdvZGIuY29tIl0sIm5vbmNlIjoiZ2RmaGpqMzI0ZWhqMjNrNCIsIm1vbmdvZGItcm9sZXMiOlsibXlSZWFkUm9sZSJdfQ.
+nhM7C5oHy1jRT4we1oFBpzzBQ7e8ccZdUvtua6d5C4wg0W2Kf3Vf2ze7VTRzq1CdIeemlx0YuzxNoE1ujZI9W9zRZJmBWhahZHG-MYqtFioz-fZvAjHdK8VhLKF2wrvRBlFDNbJtPzIdF3dT1hnfYv4ASwCgEVZJA2CrPmTTnbgVoqWhyiGUWK-DtNhHRVVIXjXkV-f6bor2Oipp0EP2ukY30LNnBQm-cBIB01Q91PZHRHkG\_\_CtayFILWSdpZPGwOlO3yjUVw0lMdoAYamUPGfjNOQooFyevdHNGuvbh8nqQPgf5ZmRYP7EUJ9\_DipoV4q90TMHQi9pXjc72zSLJg
+```
+
+As described in [RFC 7515 Section 7.1](https://www.rfc-editor.org/rfc/rfc7515#section-7.1),
+this is divided into three sections delimited by periods.
+
+### JWSHeader
+
+The first section in our example is the `base64url::encode()` output of the `JWSHeader`
+represented as `JSON`, so it would decode as:
+
+```json
+{ "typ": "JWT", "alg": "RS256", "kid": "custom-key-1" }
+```
+
+This tells us that the payload in the second field of the compact serialized signature is a `JWT`
+(this is currently expected to always be the case).
+We also see that the `alg`orithm for signing uses [`RS256`](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.3) (`RSASSA-PKCS1-v1_5 using SHA-256`) and that the signature can be verified using the key material associated with `custom-key-1`.
+
+The IDL struct `JWSHeader` may be used to parse this section for access to the relevant `alg` and `kid` fields.
+* See also: [RFC 7515 Section 4](https://www.rfc-editor.org/rfc/rfc7515#section-4) JOSE Header
+
+### JWT
+
+In the second of the three sections, we have the `JWT` token body itself,
+again encoded using `base64url` on a `JSON` representation.
+So in this example, it would decode as:
+
+```json
+{
+ "iss": "https://test.kernel.mongodb.com/oidc/issuer1",
+ "sub": "user1@mongodb.com",
+ "nbf": 1661374077,
+ "exp": 2147483647,
+ "aud": [
+ "jwt@kernel.mongodb.com"
+ ],
+ "nonce": "gdfhjj324ehj23k4",
+ "mongodb-roles": [
+ "myReadRole"
+ ]
+}
+```
+
+The IDL struct `JWT` will be used to parse this section by [`JWSValidatedToken`](#jwsvalidatedtoken),
+however token payloads SHOULD NOT be inspected without processing them through
+the validating infrastructure.
+* See [RFC 7519 Section 4](https://www.rfc-editor.org/rfc/rfc7519#section-4) JWT Claims
+
+Note that this token payload contains an additional field not defined by any RFC or ietf-draft.
+The content of this, or any other unknown fields is treated as opaque and ignored by
+[`JWSValidatedToken`](#jwsvalidatedtoken) as applications are permitted to define additional fields
+provided they are uniquely named.
+
+For information on how a field like `mongodb-roles` is used, refer to the `MONGODB-OIDC architecture guide`.
+
+### JWS Signature
+
+The third and final section of a JWS Compact Serialization is a non human-readable output of a
+cryptographic signing operation as defined by the payload's `JWSHeader`.
+
+In this case, `RS256` is specified as the signing `alg`orithm which roughly translates to
+computing the SHA256 digest of the header, delimiting period, and payload sections,
+then transforming the signature output (with PKCS#1v1.5 padding) using an RSA private key.
+We validate this signature by transforming the digest output back out using our RSA public key
+and comparing that to our locally produced digest output.
+
+The heavy lifting of this operation is handled internally by OpenSSL on linux.
diff --git a/src/mongo/crypto/README.md b/src/mongo/crypto/README.md
new file mode 100644
index 00000000000..e2088ca48b1
--- /dev/null
+++ b/src/mongo/crypto/README.md
@@ -0,0 +1,3 @@
+# MongoDB Crypto library architecture guides
+
+* [JSON Web Tokens (JWT)](README.JWT.md)