/// <summary>Returns a new instance of <see cref="Jwks"/>.</summary> /// <param name="issuer">The issuer of the keys.</param> /// <param name="json">a string that contains JSON Web Key parameters in JSON format.</param> public static Jwks FromJson(string issuer, ReadOnlySpan <byte> json) { // a JWKS is : // { // "keys": [ // { jwk1 }, // { jwk2 }, // ... // ] // } var jwks = new Jwks(issuer); var reader = new Utf8JsonReader(json, true, default); if (reader.Read() && reader.TokenType is JsonTokenType.StartObject && reader.Read()) { while (reader.TokenType is JsonTokenType.PropertyName) { var propertyName = reader.ValueSpan; if (propertyName.Length == 4) { ref byte propertyNameRef = ref MemoryMarshal.GetReference(propertyName); if (IntegerMarshal.ReadUInt32(ref propertyNameRef) == keys /* keys */) { if (reader.Read() && reader.TokenType is JsonTokenType.StartArray) { while (reader.Read() && reader.TokenType is JsonTokenType.StartObject) { Jwk jwk = Jwk.FromJsonReader(ref reader); jwks.Add(jwk); } if (!(reader.TokenType is JsonTokenType.EndArray) || !reader.Read()) { ThrowHelper.ThrowInvalidOperationException_MalformedJwks(); } } continue; } } reader.Read(); if (reader.TokenType >= JsonTokenType.String && reader.TokenType <= JsonTokenType.Null) { reader.Read(); } else if (reader.TokenType == JsonTokenType.StartObject) { JsonParser.ConsumeJsonObject(ref reader); } else if (reader.TokenType == JsonTokenType.StartArray) { JsonParser.ConsumeJsonArray(ref reader); } else { ThrowHelper.ThrowInvalidOperationException_MalformedJwks(); } } }
/// <summary>Parses the <see cref="ReadOnlySpan{T}"/> into its <see cref="EncryptionAlgorithm"/> representation.</summary> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out EncryptionAlgorithm?algorithm) { if (value.Length == 13) { switch (IntegerMarshal.ReadUInt64(value)) { case _A128CBC_ when IntegerMarshal.ReadUInt64(value, 5) == _BC_HS256: algorithm = A128CbcHS256; goto Found; case _A192CBC_ when IntegerMarshal.ReadUInt64(value, 5) == _BC_HS384: algorithm = A192CbcHS384; goto Found; case _A256CBC_ when IntegerMarshal.ReadUInt64(value, 5) == _BC_HS512: algorithm = A256CbcHS512; goto Found; } } else if (value.Length == 7) { switch (IntegerMarshal.ReadUInt56(value)) { case _A128GCM: algorithm = A128Gcm; goto Found; case _A192GCM: algorithm = A192Gcm; goto Found; case _A256GCM: algorithm = A256Gcm; goto Found; } } #if NET5_0_OR_GREATER Unsafe.SkipInit(out algorithm); #else algorithm = default; #endif return(false); Found: return(true); }
/// <summary>Parse the <see cref="ReadOnlySpan{T}"/> into its <see cref="SignatureAlgorithm"/> representation.</summary> /// <param name="value"></param> /// <param name="algorithm"></param> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out CompressionAlgorithm?algorithm) { if (value.Length == 3) { var zip = IntegerMarshal.ReadUInt24(value); if (zip == DEF) { algorithm = Def; return(true); } } algorithm = null; return(false); }
/// <summary>Parses the <see cref="ReadOnlySpan{T}"/> into its <see cref="EncryptionAlgorithm"/> representation.</summary> /// <param name="value"></param> /// <param name="algorithm"></param> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out EncryptionAlgorithm?algorithm) { if (value.Length == 13) { switch (IntegerMarshal.ReadUInt64(value)) { case _A128CBC_ when IntegerMarshal.ReadUInt64(value, 5) == _BC_HS256: algorithm = A128CbcHS256; goto Found; case _A192CBC_ when IntegerMarshal.ReadUInt64(value, 5) == _BC_HS384: algorithm = A192CbcHS384; goto Found; case _A256CBC_ when IntegerMarshal.ReadUInt64(value, 5) == _BC_HS512: algorithm = A256CbcHS512; goto Found; } } else if (value.Length == 7) { switch (IntegerMarshal.ReadUInt56(value)) { case _A128GCM: algorithm = A128Gcm; goto Found; case _A192GCM: algorithm = A192Gcm; goto Found; case _A256GCM: algorithm = A256Gcm; goto Found; } } algorithm = null; return(false); Found: return(true); }
/// <summary>Parse the <see cref="ReadOnlySpan{T}"/> into its <see cref="SignatureAlgorithm"/> representation.</summary> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out CompressionAlgorithm?algorithm) { if (value.Length == 3) { var zip = IntegerMarshal.ReadUInt24(value); if (zip == DEF) { algorithm = Def; return(true); } } #if NET5_0_OR_GREATER Unsafe.SkipInit(out algorithm); #else algorithm = default; #endif return(false); }
/// <summary> /// Cast the <see cref="ReadOnlySpan{T}"/> into its <see cref="EncryptionAlgorithm"/> representation. /// </summary> /// <param name="value"></param> /// <param name="algorithm"></param> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out EncryptionAlgorithm?algorithm) { if (value.Length == 13) { ref byte refValue = ref MemoryMarshal.GetReference(value); ulong endValue = IntegerMarshal.ReadUInt64(ref refValue, 5); switch (IntegerMarshal.ReadUInt64(ref refValue)) { case A128CBC_ when endValue == BC_HS256: algorithm = Aes128CbcHmacSha256; return(true); case A192CBC_ when endValue == BC_HS384: algorithm = Aes192CbcHmacSha384; return(true); case A256CBC_ when endValue == BC_HS512: algorithm = Aes256CbcHmacSha512; return(true); } }
/// <summary> /// Returns a new instance of <see cref="Jwks"/>. /// </summary> /// <param name="json">a string that contains JSON Web Key parameters in JSON format.</param> /// <returns><see cref="Jwks"/></returns> public static Jwks FromJson(ReadOnlySpan <byte> json) { // a JWKS is : // { // "keys": [ // { jwk1 }, // { jwk2 }, // ... // ] // } var jwks = new Jwks(); var reader = new Utf8JsonReader(json, true, default); if (reader.Read() && reader.TokenType is JsonTokenType.StartObject && reader.Read() && reader.TokenType is JsonTokenType.PropertyName) { var propertyName = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; if (propertyName.Length == 4) { ref byte propertyNameRef = ref MemoryMarshal.GetReference(propertyName); if (IntegerMarshal.ReadUInt32(ref propertyNameRef) == keys /* keys */) { if (reader.Read() && reader.TokenType is JsonTokenType.StartArray) { while (reader.Read() && reader.TokenType is JsonTokenType.StartObject) { Jwk jwk = Jwk.FromJsonReader(ref reader); jwks.Add(jwk); } if (!(reader.TokenType is JsonTokenType.EndArray) || !reader.Read()) { ThrowHelper.ThrowInvalidOperationException_MalformedJwks(); } } } } }
/// <summary> /// Gets the claim for a specified key in the current <see cref="JwtPayload"/>. /// </summary> /// <param name="key"></param> /// <returns></returns> public object?this[ReadOnlySpan <byte> key] { get { if (key.Length == 3) { switch (IntegerMarshal.ReadUInt24(key)) { case JwtPayloadParser.Aud: return(_aud); case JwtPayloadParser.Iss: return(_iss); case JwtPayloadParser.Jti: return(_jti); case JwtPayloadParser.Exp: return(_exp); case JwtPayloadParser.Iat: return(_iat); case JwtPayloadParser.Nbf: return(_nbf); case JwtPayloadParser.Sub: return(_sub); } } if (_inner is null) { return(null); } return(_inner.TryGetValue(key, out var value) ? value.Value : null); } }
/// <summary> /// Cast the <see cref="ReadOnlySpan{T}"/> into its <see cref="KeyManagementAlgorithm"/> representation. /// </summary> /// <param name="value"></param> /// <param name="algorithm"></param> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out KeyManagementAlgorithm?algorithm) { switch (value.Length) { case 3 when IntegerMarshal.ReadUInt24(value) == dir: algorithm = Direct; return(true); case 6 when IntegerMarshal.ReadUInt16(value, 4) == KW: switch (IntegerMarshal.ReadUInt32(value)) { case A128: algorithm = Aes128KW; return(true); case A192: algorithm = Aes192KW; return(true); case A256: algorithm = Aes256KW; return(true); } break; case 6 when IntegerMarshal.ReadUInt32(value) == RSA1 && IntegerMarshal.ReadUInt16(value, 4) == _5: algorithm = RsaPkcs1; return(true); case 7 when IntegerMarshal.ReadUInt56(value) == ECDH_ES: algorithm = EcdhEs; return(true); case 8 when IntegerMarshal.ReadUInt64(value) == RSA_OAEP: algorithm = RsaOaep; return(true); case 9 when IntegerMarshal.ReadUInt8(value) == (byte) 'A': switch (IntegerMarshal.ReadUInt64(value, 1)) { case _128GCMKW: algorithm = Aes128GcmKW; return(true); case _192GCMKW: algorithm = Aes192GcmKW; return(true); case _256GCMKW: algorithm = Aes256GcmKW; return(true); } break; case 12 when IntegerMarshal.ReadUInt64(value) == RSA_OAEP: switch (IntegerMarshal.ReadUInt32(value, 8)) { case _256: algorithm = RsaOaep256; return(true); case _384: algorithm = RsaOaep384; return(true); case _512: algorithm = RsaOaep512; return(true); } break; case 14 when IntegerMarshal.ReadUInt64(value) == ECDH_ES_: switch (IntegerMarshal.ReadUInt64(value, 6)) { case S_A128KW: algorithm = EcdhEsAes128KW; return(true); case S_A192KW: algorithm = EcdhEsAes192KW; return(true); case S_A256KW: algorithm = EcdhEsAes256KW; return(true); } break; // Special case for escaped 'ECDH-ES\u002bAxxxKW' case 19 when IntegerMarshal.ReadUInt64(value) == ECDH_ES_UTF8 /* ECDH-ES\ */: switch (IntegerMarshal.ReadUInt64(value, 8)) { case u002bA12 when IntegerMarshal.ReadUInt32(value, 15) == _28KW: algorithm = EcdhEsAes128KW; return(true); case u002bA19 when IntegerMarshal.ReadUInt32(value, 15) == _92KW: algorithm = EcdhEsAes192KW; return(true); case u002bA25 when IntegerMarshal.ReadUInt32(value, 15) == _56KW: algorithm = EcdhEsAes256KW; return(true); } break; } algorithm = null; return(false); }
/// <summary>Parses the <see cref="ReadOnlySpan{T}"/> into its <see cref="SignatureAlgorithm"/> representation.</summary> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out SignatureAlgorithm?algorithm) { if (value.Length == 5) { var first = IntegerMarshal.ReadUInt8(value); switch (IntegerMarshal.ReadUInt32(value, 1)) { case _S256 when first == (byte)'H': algorithm = HS256; goto Found; case _S256 when first == (byte)'R': algorithm = RS256; goto Found; case _S256 when first == (byte)'E': algorithm = ES256; goto Found; case _S256 when first == (byte)'P': algorithm = PS256; goto Found; case _S384 when first == (byte)'H': algorithm = HS384; goto Found; case _S384 when first == (byte)'R': algorithm = RS384; goto Found; case _S384 when first == (byte)'E': algorithm = ES384; goto Found; case _S384 when first == (byte)'P': algorithm = PS384; goto Found; case _S512 when first == (byte)'H': algorithm = HS512; goto Found; case _S512 when first == (byte)'R': algorithm = RS512; goto Found; case _S512 when first == (byte)'E': algorithm = ES512; goto Found; case _S512 when first == (byte)'P': algorithm = PS512; goto Found; } } else if (value.Length == 4 && IntegerMarshal.ReadUInt32(value) == _none) { algorithm = None; goto Found; } else if (value.Length == 6 && IntegerMarshal.ReadUInt48(value) == _ES256K) { algorithm = ES256K; goto Found; } #if NET5_0_OR_GREATER Unsafe.SkipInit(out algorithm); #else algorithm = default; #endif return(false); Found: return(true); }
internal static bool TryParseHeader(ReadOnlyMemory <byte> utf8Payload, byte[]?buffer, TokenValidationPolicy policy, [NotNullWhen(true)] out JwtHeaderDocument?header, [NotNullWhen(false)] out TokenValidationError?error) { ReadOnlySpan <byte> utf8JsonSpan = utf8Payload.Span; var database = new JsonMetadata(utf8Payload.Length); int algIdx = -1; int encIdx = -1; int kidIdx = -1; int critIdx = -1; var reader = new Utf8JsonReader(utf8JsonSpan); if (reader.Read()) { JsonTokenType tokenType = reader.TokenType; if (tokenType == JsonTokenType.StartObject) { while (reader.Read()) { tokenType = reader.TokenType; int tokenStart = (int)reader.TokenStartIndex; if (tokenType == JsonTokenType.EndObject) { break; } else if (tokenType != JsonTokenType.PropertyName) { error = TokenValidationError.MalformedToken(); goto Error; } // Adding 1 to skip the start quote will never overflow Debug.Assert(tokenStart < int.MaxValue); database.Append(JsonTokenType.PropertyName, tokenStart + 1, reader.ValueSpan.Length); if (reader.ValueSpan.IndexOf((byte)'\\') != -1) { database.SetNeedUnescaping(database.Length - JsonRow.Size); } ReadOnlySpan <byte> memberName = reader.ValueSpan; reader.Read(); tokenType = reader.TokenType; tokenStart = (int)reader.TokenStartIndex; // Since the input payload is contained within a Span, // token start index can never be larger than int.MaxValue (i.e. utf8JsonSpan.Length). Debug.Assert(reader.TokenStartIndex <= int.MaxValue); if (tokenType == JsonTokenType.String) { if (memberName.Length == 3) { switch ((JwtHeaderParameters)IntegerMarshal.ReadUInt24(memberName)) { case JwtHeaderParameters.Alg: algIdx = database.Length; break; case JwtHeaderParameters.Enc: encIdx = database.Length; break; case JwtHeaderParameters.Kid: kidIdx = database.Length; break; } } // Adding 1 to skip the start quote will never overflow Debug.Assert(tokenStart < int.MaxValue); database.Append(JsonTokenType.String, tokenStart + 1, reader.ValueSpan.Length); } else if (tokenType == JsonTokenType.StartObject) { int count = Utf8JsonReaderHelper.SkipObject(ref reader); int index = database.Length; int tokenEnd = (int)reader.TokenStartIndex; database.Append(JsonTokenType.StartObject, tokenStart, tokenEnd - tokenStart + 1); database.SetNumberOfRows(index, count); } else if (tokenType == JsonTokenType.StartArray) { int count; if (memberName.Length == 4 && (JwtHeaderParameters)IntegerMarshal.ReadUInt32(memberName) == JwtHeaderParameters.Crit && !policy.IgnoreCriticalHeader) { critIdx = database.Length; if (!TryCheckCrit(ref reader, out count)) { error = TokenValidationError.MalformedToken("The 'crit' header parameter must be an array of string."); goto Error; } } else { count = Utf8JsonReaderHelper.SkipArray(ref reader); } int index = database.Length; int tokenEnd = (int)reader.TokenStartIndex; database.Append(JsonTokenType.StartArray, tokenStart, tokenEnd - tokenStart + 1); database.SetNumberOfRows(index, count); } else { Debug.Assert(tokenType >= JsonTokenType.Number && tokenType <= JsonTokenType.Null); database.Append(tokenType, tokenStart, reader.ValueSpan.Length); } } } } Debug.Assert(reader.BytesConsumed == utf8JsonSpan.Length); database.CompleteAllocations(); header = new JwtHeaderDocument(new JwtDocument(utf8Payload, database, buffer), algIdx, encIdx, kidIdx, critIdx); #if NET5_0_OR_GREATER Unsafe.SkipInit(out error); #else error = default; #endif return(true); Error: #if NET5_0_OR_GREATER Unsafe.SkipInit(out header); #else header = default; #endif return(false); }
/// <summary>Parses the <see cref="ReadOnlySpan{T}"/> into its <see cref="SignatureAlgorithm"/> representation.</summary> /// <param name="value"></param> /// <param name="algorithm"></param> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out SignatureAlgorithm?algorithm) { if (value.Length == 5) { var first = IntegerMarshal.ReadUInt8(value); switch (IntegerMarshal.ReadUInt32(value, 1)) { case _S256 when first == (byte)'H': algorithm = HS256; goto Found; case _S256 when first == (byte)'R': algorithm = RS256; goto Found; case _S256 when first == (byte)'E': algorithm = ES256; goto Found; case _S256 when first == (byte)'P': algorithm = PS256; goto Found; case _S384 when first == (byte)'H': algorithm = HS384; goto Found; case _S384 when first == (byte)'R': algorithm = RS384; goto Found; case _S384 when first == (byte)'E': algorithm = ES384; goto Found; case _S384 when first == (byte)'P': algorithm = PS384; goto Found; case _S512 when first == (byte)'H': algorithm = HS512; goto Found; case _S512 when first == (byte)'R': algorithm = RS512; goto Found; case _S512 when first == (byte)'E': algorithm = ES512; goto Found; case _S512 when first == (byte)'P': algorithm = PS512; goto Found; } } else if (value.Length == 4 && IntegerMarshal.ReadUInt32(value) == _none) { algorithm = None; goto Found; } else if (value.Length == 6 && IntegerMarshal.ReadUInt48(value) == _ES256X) { algorithm = ES256X; goto Found; } algorithm = null; return(false); Found: return(true); }
internal static bool TryParsePayload(ReadOnlyMemory <byte> utf8Payload, byte[]?buffer, TokenValidationPolicy policy, [NotNullWhen(true)] out JwtPayloadDocument?payload, [NotNullWhen(false)] out TokenValidationError?error) { ReadOnlySpan <byte> utf8JsonSpan = utf8Payload.Span; var database = new JsonMetadata(utf8Payload.Length); byte control = policy.Control; int issIdx = -1; var reader = new Utf8JsonReader(utf8JsonSpan); if (reader.Read()) { JsonTokenType tokenType = reader.TokenType; if (tokenType == JsonTokenType.StartObject) { while (reader.Read()) { tokenType = reader.TokenType; int tokenStart = (int)reader.TokenStartIndex; if (tokenType == JsonTokenType.EndObject) { break; } else if (tokenType != JsonTokenType.PropertyName) { error = TokenValidationError.MalformedToken(); goto Error; } Debug.Assert(tokenStart < int.MaxValue); database.Append(JsonTokenType.PropertyName, tokenStart + 1, reader.ValueSpan.Length); ReadOnlySpan <byte> memberName = reader.ValueSpan; if (memberName.IndexOf((byte)'\\') != -1) { database.SetNeedUnescaping(database.Length - JsonRow.Size); } reader.Read(); tokenType = reader.TokenType; tokenStart = (int)reader.TokenStartIndex; Debug.Assert(reader.TokenStartIndex <= int.MaxValue); if (tokenType == JsonTokenType.String) { if (memberName.Length == 3) { switch ((JwtClaims)IntegerMarshal.ReadUInt24(memberName)) { case JwtClaims.Aud: CheckStringAudience(ref reader, ref control, policy); break; case JwtClaims.Iss: issIdx = database.Length; CheckIssuer(ref reader, ref control, policy); break; } } Debug.Assert(tokenStart < int.MaxValue); database.Append(JsonTokenType.String, tokenStart + 1, reader.ValueSpan.Length); } else if (tokenType == JsonTokenType.Number) { if (memberName.Length == 3) { switch ((JwtClaims)IntegerMarshal.ReadUInt24(memberName)) { case JwtClaims.Exp: if (!TryCheckExpirationTime(ref reader, ref control, policy)) { error = TokenValidationError.MalformedToken("The claim 'exp' must be an integral number."); goto Error; } break; case JwtClaims.Nbf: if (!TryCheckNotBefore(ref reader, ref control, policy)) { error = TokenValidationError.MalformedToken("The claim 'nbf' must be an integral number."); goto Error; } break; } } database.Append(JsonTokenType.Number, tokenStart, reader.ValueSpan.Length); } else if (tokenType == JsonTokenType.StartObject) { int itemCount = Utf8JsonReaderHelper.SkipObject(ref reader); int tokenEnd = (int)reader.TokenStartIndex; int index = database.Length; database.Append(JsonTokenType.StartObject, tokenStart, tokenEnd - tokenStart + 1); database.SetNumberOfRows(index, itemCount); } else if (tokenType == JsonTokenType.StartArray) { int itemCount; if (memberName.Length == 3 && (JwtClaims)IntegerMarshal.ReadUInt24(memberName) == JwtClaims.Aud) { itemCount = CheckArrayAudience(ref reader, ref control, policy); } else { itemCount = Utf8JsonReaderHelper.SkipArray(ref reader); } int index = database.Length; int tokenEnd = (int)reader.TokenStartIndex; database.Append(JsonTokenType.StartArray, tokenStart, tokenEnd - tokenStart + 1); database.SetNumberOfRows(index, itemCount); } else { Debug.Assert(tokenType >= JsonTokenType.True && tokenType <= JsonTokenType.Null); database.Append(tokenType, tokenStart, reader.ValueSpan.Length); } } } } Debug.Assert(reader.BytesConsumed == utf8JsonSpan.Length); database.CompleteAllocations(); payload = new JwtPayloadDocument(new JwtDocument(utf8Payload, database, buffer), control, issIdx); #if NET5_0_OR_GREATER Unsafe.SkipInit(out error); #else error = default; #endif return(true); Error: #if NET5_0_OR_GREATER Unsafe.SkipInit(out payload); #else payload = default; #endif return(false); }
/// <summary> /// Parses the <see cref="ReadOnlySpan{T}"/> into its <see cref="SignatureAlgorithm"/> representation. /// </summary> /// <param name="value"></param> /// <param name="algorithm"></param> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out SignatureAlgorithm?algorithm) { if (value.Length == 5) { var first = IntegerMarshal.ReadUInt8(value); switch (IntegerMarshal.ReadUInt32(value, 1)) { case S256 when first == (byte)'H': algorithm = HmacSha256; return(true); case S256 when first == (byte)'R': algorithm = RsaSha256; return(true); case S256 when first == (byte)'E': algorithm = EcdsaSha256; return(true); case S256 when first == (byte)'P': algorithm = RsaSsaPssSha256; return(true); case S384 when first == (byte)'H': algorithm = HmacSha384; return(true); case S384 when first == (byte)'R': algorithm = RsaSha384; return(true); case S384 when first == (byte)'E': algorithm = EcdsaSha384; return(true); case S384 when first == (byte)'P': algorithm = RsaSsaPssSha384; return(true); case S512 when first == (byte)'H': algorithm = HmacSha512; return(true); case S512 when first == (byte)'R': algorithm = RsaSha512; return(true); case S512 when first == (byte)'E': algorithm = EcdsaSha512; return(true); case S512 when first == (byte)'P': algorithm = RsaSsaPssSha512; return(true); } } else if (value.Length == 4 && IntegerMarshal.ReadUInt32(value) == none) { algorithm = None; return(true); } algorithm = null; return(false); }
/// <summary> /// Parses the UTF-8 <paramref name="buffer"/> as JSON and returns a <see cref="JwtHeader"/>. /// </summary> /// <param name="buffer"></param> /// <param name="policy"></param> public static JwtHeader ParseHeader(ReadOnlySpan <byte> buffer, TokenValidationPolicy policy) { Utf8JsonReader reader = new Utf8JsonReader(buffer, isFinalBlock: true, state: default); if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowFormatException_MalformedJson(); } var header = new JwtHeader(); while (reader.Read()) { if (!(reader.TokenType is JsonTokenType.PropertyName)) { break; } var name = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; reader.Read(); var type = reader.TokenType; if (name.Length == 3) { if (reader.TokenType == JsonTokenType.String) { var refName = IntegerMarshal.ReadUInt24(name); switch (refName) { case Alg: var alg = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; if (SignatureAlgorithm.TryParse(alg, out var signatureAlgorithm)) { header.SignatureAlgorithm = signatureAlgorithm; } else if (KeyManagementAlgorithm.TryParse(alg, out var keyManagementAlgorithm)) { header.KeyManagementAlgorithm = keyManagementAlgorithm; } else if (SignatureAlgorithm.TryParseSlow(ref reader, out signatureAlgorithm)) { header.SignatureAlgorithm = signatureAlgorithm; } else if (KeyManagementAlgorithm.TryParseSlow(ref reader, out keyManagementAlgorithm)) { header.KeyManagementAlgorithm = keyManagementAlgorithm; } else { header.SignatureAlgorithm = SignatureAlgorithm.Create(reader.GetString()); } continue; case Enc: var enc = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; if (EncryptionAlgorithm.TryParse(enc, out var encryptionAlgorithm)) { header.EncryptionAlgorithm = encryptionAlgorithm; } else if (EncryptionAlgorithm.TryParseSlow(ref reader, out encryptionAlgorithm)) { header.EncryptionAlgorithm = encryptionAlgorithm; } else { header.EncryptionAlgorithm = EncryptionAlgorithm.Create(reader.GetString()); } continue; case Zip: var zip = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; if (CompressionAlgorithm.TryParse(zip, out var compressionAlgorithm)) { header.CompressionAlgorithm = compressionAlgorithm; } else if (CompressionAlgorithm.TryParseSlow(ref reader, out compressionAlgorithm)) { header.CompressionAlgorithm = compressionAlgorithm; } else { header.CompressionAlgorithm = CompressionAlgorithm.Create(reader.GetString()); } continue; case Cty: header.Cty = reader.GetString(); continue; case Typ: header.Typ = reader.GetString(); continue; case Kid: header.Kid = reader.GetString(); continue; } } } else if (name.Length == 4) { if (reader.TokenType == JsonTokenType.StartArray && IntegerMarshal.ReadUInt32(name) == Crit) { var handlers = policy.CriticalHandlers; if (handlers.Count != 0) { var criticalHeaderHandlers = new List <KeyValuePair <string, ICriticalHeaderHandler> >(handlers.Count); var criticals = new List <JwtValue>(); while (reader.Read() && reader.TokenType == JsonTokenType.String) { string criticalHeader = reader.GetString(); criticals.Add(new JwtValue(criticalHeader)); if (handlers.TryGetValue(criticalHeader, out var handler)) { criticalHeaderHandlers.Add(new KeyValuePair <string, ICriticalHeaderHandler>(criticalHeader, handler)); } else { criticalHeaderHandlers.Add(new KeyValuePair <string, ICriticalHeaderHandler>(criticalHeader, null !)); } } header.CriticalHeaderHandlers = criticalHeaderHandlers; if (reader.TokenType != JsonTokenType.EndArray) { ThrowHelper.ThrowFormatException_MalformedJson("The 'crit' header parameter must be an array of string."); } header.Inner.Add(name, new JwtArray(criticals)); } else { var criticals = new List <JwtValue>(); while (reader.Read() && reader.TokenType == JsonTokenType.String) { string criticalHeader = reader.GetString(); criticals.Add(new JwtValue(criticalHeader)); } if (reader.TokenType != JsonTokenType.EndArray) { ThrowHelper.ThrowFormatException_MalformedJson("The 'crit' header parameter must be an array of string."); } header.Inner.Add(name, new JwtArray(criticals)); } continue; } } switch (type) { case JsonTokenType.StartObject: header.Inner.Add(name, JsonParser.ReadJsonObject(ref reader)); break; case JsonTokenType.StartArray: header.Inner.Add(name, JsonParser.ReadJsonArray(ref reader)); break; case JsonTokenType.String: header.Inner.Add(name, reader.GetString()); break; case JsonTokenType.True: header.Inner.Add(name, true); break; case JsonTokenType.False: header.Inner.Add(name, false); break; case JsonTokenType.Null: header.Inner.Add(name); break; case JsonTokenType.Number: if (reader.TryGetInt64(out long longValue)) { header.Inner.Add(name, longValue); } else { header.Inner.Add(name, reader.GetDouble()); } break; default: ThrowHelper.ThrowFormatException_MalformedJson(); break; } } if (!(reader.TokenType is JsonTokenType.EndObject)) { ThrowHelper.ThrowFormatException_MalformedJson(); } return(header); }
/// <summary>Parses the <see cref="ReadOnlySpan{T}"/> into its <see cref="KeyManagementAlgorithm"/> representation.</summary> public static bool TryParse(ReadOnlySpan <byte> value, [NotNullWhen(true)] out KeyManagementAlgorithm?algorithm) { switch (value.Length) { case 3 when IntegerMarshal.ReadUInt24(value) == _dir: algorithm = Dir; goto Found; case 6 when IntegerMarshal.ReadUInt16(value, 4) == _KW: switch (IntegerMarshal.ReadUInt32(value)) { case _A128: algorithm = A128KW; goto Found; case _A192: algorithm = A192KW; goto Found; case _A256: algorithm = A256KW; goto Found; } break; case 6 when IntegerMarshal.ReadUInt32(value) == _RSA1 && IntegerMarshal.ReadUInt16(value, 4) == __5: algorithm = Rsa1_5; goto Found; case 7 when IntegerMarshal.ReadUInt56(value) == _ECDH_ES: algorithm = EcdhEs; goto Found; case 8 when IntegerMarshal.ReadUInt64(value) == _RSA_OAEP: algorithm = RsaOaep; goto Found; case 9 when IntegerMarshal.ReadUInt8(value) == (byte) 'A': switch (IntegerMarshal.ReadUInt64(value, 1)) { case __128GCMKW: algorithm = A128GcmKW; goto Found; case __192GCMKW: algorithm = A192GcmKW; goto Found; case __256GCMKW: algorithm = A256GcmKW; goto Found; } break; case 12 when IntegerMarshal.ReadUInt64(value) == _RSA_OAEP: switch (IntegerMarshal.ReadUInt32(value, 8)) { case __256: algorithm = RsaOaep256; goto Found; case __384: algorithm = RsaOaep384; goto Found; case __512: algorithm = RsaOaep512; goto Found; } break; case 14 when IntegerMarshal.ReadUInt64(value) == _ECDH_ES_: switch (IntegerMarshal.ReadUInt64(value, 6)) { case _S_A128KW: algorithm = EcdhEsA128KW; goto Found; case _S_A192KW: algorithm = EcdhEsA192KW; goto Found; case _S_A256KW: algorithm = EcdhEsA256KW; goto Found; } break; // 'PBES2-HS384+A192KW' case 18 when IntegerMarshal.ReadUInt64(value) == 6001096197639848528uL && /* PBES2-HS 384+A192KW*/ IntegerMarshal.ReadUInt16(value, 16) == _KW: switch (IntegerMarshal.ReadUInt64(value, 8)) { case 4049353170927105330uL: algorithm = Pbes2HS256A128KW; goto Found; case 3618977931536382003uL: algorithm = Pbes2HS384A192KW; goto Found; case 3906083507292746037: algorithm = Pbes2HS512A256KW; goto Found; } break; // Special case for escaped 'ECDH-ES\u002bAxxxKW' case 19 when IntegerMarshal.ReadUInt64(value) == _ECDH_ES_UTF8 /* ECDH-ES\ */: switch (IntegerMarshal.ReadUInt64(value, 8) | u002bUpperMask) { case _u002bA12 when IntegerMarshal.ReadUInt32(value, 15) == __28KW: algorithm = EcdhEsA128KW; goto Found; case _u002bA19 when IntegerMarshal.ReadUInt32(value, 15) == __92KW: algorithm = EcdhEsA192KW; goto Found; case _u002bA25 when IntegerMarshal.ReadUInt32(value, 15) == __56KW: algorithm = EcdhEsA256KW; goto Found; } break; } algorithm = null; return(false); Found: return(true); }
/// <summary> /// Parses the UTF-8 <paramref name="buffer"/> as JSON and returns a <see cref="JwtPayload"/>. /// </summary> /// <param name="buffer"></param> /// <param name="policy"></param> public static JwtPayload ParsePayload(ReadOnlySpan <byte> buffer, TokenValidationPolicy policy) { Utf8JsonReader reader = new Utf8JsonReader(buffer, isFinalBlock: true, state: default); if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject) { ThrowHelper.ThrowFormatException_MalformedJson(); } var payload = new JwtPayload(); byte control = policy.ValidationControl; while (reader.Read() && reader.TokenType is JsonTokenType.PropertyName) { var name = reader.ValueSpan /* reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan */; reader.Read(); var type = reader.TokenType; if (name.Length == 3) { uint nameValue = IntegerMarshal.ReadUInt24(name); switch (nameValue) { case Aud: if (type == JsonTokenType.String) { if (policy.RequireAudience) { var audiencesBinary = policy.RequiredAudiencesBinary; var audiences = policy.RequiredAudiences; for (int i = 0; i < audiencesBinary.Length; i++) { if (reader.ValueTextEquals(audiencesBinary[i])) { payload.Aud = new[] { audiences[i] }; control &= unchecked ((byte)~TokenValidationPolicy.AudienceFlag); break; } } control &= unchecked ((byte)~JwtPayload.MissingAudienceFlag); } else { payload.Aud = new[] { reader.GetString() }; } } else if (type == JsonTokenType.StartArray) { if (policy.RequireAudience) { var audiences = new List <string>(); while (reader.Read() && reader.TokenType == JsonTokenType.String) { var requiredAudiences = policy.RequiredAudiencesBinary; for (int i = 0; i < requiredAudiences.Length; i++) { if (reader.ValueTextEquals(requiredAudiences[i])) { control &= unchecked ((byte)~TokenValidationPolicy.AudienceFlag); break; } } audiences.Add(reader.GetString()); control &= unchecked ((byte)~JwtPayload.MissingAudienceFlag); } if (reader.TokenType != JsonTokenType.EndArray) { ThrowHelper.ThrowFormatException_MalformedJson("The 'aud' claim must be an array of string or a string."); } payload.Aud = audiences.ToArray(); } else { payload.Aud = JsonParser.ReadStringArray(ref reader); } } else { ThrowHelper.ThrowFormatException_MalformedJson("The 'aud' claim must be an array of string or a string."); } continue; case Iss: if (policy.RequireIssuer) { if (reader.ValueTextEquals(policy.RequiredIssuerBinary)) { payload.Iss = policy.RequiredIssuer; control &= unchecked ((byte)~TokenValidationPolicy.IssuerFlag); } else { control &= unchecked ((byte)~JwtPayload.MissingIssuerFlag); } } else { payload.Iss = reader.GetString(); } continue; case Exp: if (!reader.TryGetInt64(out long longValue)) { ThrowHelper.ThrowFormatException_MalformedJson("The claim 'exp' must be an integral number."); } if (policy.RequireExpirationTime) { control &= unchecked ((byte)~JwtPayload.MissingExpirationFlag); } if (longValue >= EpochTime.UtcNow - policy.ClockSkew) { control &= unchecked ((byte)~JwtPayload.ExpiredFlag); } payload.Exp = longValue; continue; case Iat: if (!reader.TryGetInt64(out longValue)) { ThrowHelper.ThrowFormatException_MalformedJson("The claim 'iat' must be an integral number."); } payload.Iat = longValue; continue; case Nbf: if (!reader.TryGetInt64(out longValue)) { ThrowHelper.ThrowFormatException_MalformedJson("The claim 'nbf' must be an integral number."); } // the 'nbf' claim is not common. The 2nd call to EpochTime.UtcNow should be rare. if (longValue <= EpochTime.UtcNow + policy.ClockSkew) { control &= unchecked ((byte)~JwtPayload.NotYetFlag); } payload.Nbf = longValue; continue; case Jti: payload.Jti = reader.GetString(); continue; case Sub: payload.Sub = reader.GetString(); continue; } } switch (type) { case JsonTokenType.StartObject: payload.Inner.Add(name, JsonParser.ReadJsonObject(ref reader)); break; case JsonTokenType.StartArray: payload.Inner.Add(name, JsonParser.ReadJsonArray(ref reader)); break; case JsonTokenType.String: payload.Inner.Add(name, reader.GetString()); break; case JsonTokenType.True: payload.Inner.Add(name, true); break; case JsonTokenType.False: payload.Inner.Add(name, false); break; case JsonTokenType.Null: payload.Inner.Add(name); break; case JsonTokenType.Number: long longValue; if (reader.TryGetInt64(out longValue)) { payload.Inner.Add(name, longValue); } else { payload.Inner.Add(name, reader.GetDouble()); } break; default: ThrowHelper.ThrowFormatException_MalformedJson(); break; } } if (!(reader.TokenType is JsonTokenType.EndObject)) { ThrowHelper.ThrowFormatException_MalformedJson(); } payload.ValidationControl = control; return(payload); }