Skip to content

Commit

Permalink
Import JWT addon changes.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisl8 committed Dec 10, 2023
1 parent 78ba444 commit 640ffef
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 151 deletions.
98 changes: 39 additions & 59 deletions addons/jwt/src/JWTAlgorithm.gd
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
extends RefCounted
class_name JWTAlgorithm

enum Type { HMAC1, HMAC256, RSA256 }
enum Type {
HMAC1,
HMAC256,
RSA256
}

var _alg: int = -1
var _secret: String = ""
Expand All @@ -10,72 +14,48 @@ var crypto: Crypto = Crypto.new()
var _public_crypto: CryptoKey = CryptoKey.new()
var _private_crypto: CryptoKey = CryptoKey.new()


func get_name() -> String:
match _alg:
# Note: HS1 is not secure and should be removed.
Type.HMAC1:
return "HSA1"
Type.HMAC256:
return "HS256"
Type.RSA256:
return "RS256"
_:
return ""
match _alg:
# Note: HS1 is not secure and should be removed.
Type.HMAC1: return "HSA1"
Type.HMAC256: return "HS256"
Type.RSA256: return "RS256"
_: return ""


func _digest(ctx_type: HashingContext.HashType, data: PackedByteArray) -> PackedByteArray:
var ctx: HashingContext = HashingContext.new()
# Start a SHA-256 context.
ctx.start(ctx_type)
# Check that file exists.
ctx.update(data)
# Get the computed hash.
return ctx.finish()
var ctx = HashingContext.new()
# Start a SHA-256 context.
ctx.start(ctx_type)
# Check that file exists.
ctx.update(data)
# Get the computed hash.
return ctx.finish()


func sign(text: String) -> PackedByteArray:
var signature_bytes: PackedByteArray = []
match self._alg:
Type.HMAC1:
signature_bytes = self.crypto.hmac_digest(
HashingContext.HASH_SHA1, self._secret.to_utf8_buffer(), text.to_utf8_buffer()
)
Type.HMAC256:
signature_bytes = self.crypto.hmac_digest(
HashingContext.HASH_SHA256, self._secret.to_utf8_buffer(), text.to_utf8_buffer()
)
Type.RSA256:
signature_bytes = self.crypto.sign(
HashingContext.HASH_SHA256, text.sha256_buffer(), self._private_crypto
)
return signature_bytes
var signature_bytes: PackedByteArray = []
match self._alg:
Type.HMAC1:
signature_bytes = self.crypto.hmac_digest(HashingContext.HASH_SHA1, self._secret.to_utf8_buffer(), text.to_utf8_buffer())
Type.HMAC256:
signature_bytes = self.crypto.hmac_digest(HashingContext.HASH_SHA256, self._secret.to_utf8_buffer(), text.to_utf8_buffer())
Type.RSA256:
signature_bytes = self.crypto.sign(HashingContext.HASH_SHA256, text.sha256_buffer(), self._private_crypto)
return signature_bytes


# TODO: Debug this.
func verify(jwt: JWTDecoder) -> bool:
var signature_bytes: PackedByteArray = []
match self._alg:
Type.HMAC1:
signature_bytes = self.crypto.hmac_digest(
HashingContext.HASH_SHA1,
self._secret.to_utf8_buffer(),
(jwt.parts[0] + "." + jwt.parts[1]).to_utf8_buffer()
)
Type.HMAC256:
signature_bytes = self.crypto.hmac_digest(
HashingContext.HASH_SHA256,
self._secret.to_utf8_buffer(),
(jwt.parts[0] + "." + jwt.parts[1]).to_utf8_buffer()
)
Type.RSA256:
# type, hash, sig, key
print()
return self.crypto.verify(
HashingContext.HASH_SHA256,
(jwt.parts[0] + "." + jwt.parts[1]).sha256_buffer(),
JWTUtils.base64URL_decode(jwt.parts[2]),
self._public_crypto
)
#signature_bytes = self.crypto.verify(self._public_crypto, .to_utf8_buffer())
return jwt.parts[2] == JWTUtils.base64URL_encode(signature_bytes)
var signature_bytes: PackedByteArray = []
match self._alg:
Type.HMAC1:
signature_bytes = self.crypto.hmac_digest(HashingContext.HASH_SHA1, self._secret.to_utf8_buffer(), (jwt.parts[0]+"."+jwt.parts[1]).to_utf8_buffer())
Type.HMAC256:
signature_bytes = self.crypto.hmac_digest(HashingContext.HASH_SHA256, self._secret.to_utf8_buffer(), (jwt.parts[0]+"."+jwt.parts[1]).to_utf8_buffer())
Type.RSA256:
# type, hash, sig, key
print()
return self.crypto.verify(HashingContext.HASH_SHA256, (jwt.parts[0]+"."+jwt.parts[1]).sha256_buffer(), JWTUtils.base64URL_decode(jwt.parts[2]), self._public_crypto)
#signature_bytes = self.crypto.verify(self._public_crypto, .to_utf8_buffer())
return jwt.parts[2] == JWTUtils.base64URL_encode(signature_bytes)
2 changes: 1 addition & 1 deletion addons/jwt/src/JWTBaseBuilder.gd
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,5 @@ func with_payload(claims: Dictionary) -> JWTBaseBuilder:
add_claim(claim, claims[claim])
return self

func add_claim(claim_name: String, claim_value) -> void:
func add_claim(_claim_name: String, _claim_value) -> void:
return
14 changes: 6 additions & 8 deletions addons/jwt/src/JWTBuilder.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class_name JWTBuilder

var crypto: Crypto = Crypto.new()

var algorithm: JWTAlgorithm
var _algorithm: JWTAlgorithm
var header_claims: Dictionary = { alg = "", typ = "JWT" }
var payload_claims: Dictionary
var secret: String
Expand All @@ -12,8 +12,8 @@ func _init(algorithm_param: JWTAlgorithm = null, header_claims_param: Dictionary
if not header_claims_param.is_empty(): self.header_claims = header_claims_param
if not payload_claims_param.is_empty(): self.payload_claims = payload_claims_param
if algorithm_param != null:
self.algorithm = algorithm_param
self.header_claims.alg = self.algorithm.get_name()
self._algorithm = algorithm_param
self.header_claims.alg = self._algorithm.get_name()

func add_claim(name: String, value) -> void:
match typeof(value):
Expand All @@ -33,13 +33,11 @@ func add_claim(name: String, value) -> void:


func sign(algorithm: JWTAlgorithm = null) -> String:
if algorithm != null: self.algorithm = algorithm
if algorithm != null: self._algorithm = algorithm
assert(algorithm != null, "Can't sign a JWT without an Algorithm")
with_algorithm(algorithm.get_name())
var header_serializer : JSON = JSON.new()
var header: String = JWTUtils.base64URL_encode(header_serializer.stringify(self.header_claims).to_utf8_buffer())
var payload_serializer : JSON = JSON.new()
var payload: String = JWTUtils.base64URL_encode(payload_serializer.stringify(self.payload_claims).to_utf8_buffer())
var header: String = JWTUtils.base64URL_encode(JSON.stringify(self.header_claims).to_utf8_buffer())
var payload: String = JWTUtils.base64URL_encode(JSON.stringify(self.payload_claims).to_utf8_buffer())
var signature_bytes: PackedByteArray = algorithm.sign(header+"."+payload)
var signature: String = JWTUtils.base64URL_encode(signature_bytes)
return "%s.%s.%s" % [header, payload, signature]
92 changes: 35 additions & 57 deletions addons/jwt/src/JWTDecoder.gd
Original file line number Diff line number Diff line change
Expand Up @@ -5,102 +5,80 @@ var parts: Array = []
var header_claims: Dictionary = {}
var payload_claims: Dictionary = {}


func _init(jwt: String):
self.parts = jwt.split(".")
var header: PackedByteArray = JWTUtils.base64URL_decode(self.parts[0])
var payload: PackedByteArray = JWTUtils.base64URL_decode(self.parts[1])
self.header_claims = _parse_json(header.get_string_from_utf8())
self.payload_claims = _parse_json(payload.get_string_from_utf8())

self.parts = jwt.split(".")
var header: PackedByteArray = JWTUtils.base64URL_decode(self.parts[0])
var payload: PackedByteArray = JWTUtils.base64URL_decode(self.parts[1])
self.header_claims = _parse_json(header.get_string_from_utf8())
self.payload_claims = _parse_json(payload.get_string_from_utf8())

func _parse_json(field) -> Dictionary:
var parse_result: JSON = JSON.new()
if parse_result.parse(field) != OK:
return {}
var data = parse_result.get_data()
_maybe_pack_array(data, JWTClaims.Public.AUDIENCE)
return data


var parse_result = JSON.new()
if parse_result.parse(field) != OK:
return {}
var data = parse_result.get_data()
_maybe_pack_array(data, JWTClaims.Public.AUDIENCE)
return data

func _maybe_pack_array(dict, key):
if !dict.has(key):
return
if typeof(dict[key]) == TYPE_ARRAY:
dict[key] = PackedStringArray(dict[key])

if !dict.has(key):
return
if typeof(dict[key]) == TYPE_ARRAY:
dict[key] = PackedStringArray(dict[key])

func get_algorithm() -> String:
return self.header_claims.get(JWTClaims.Public.ALGORITHM, "null")

return self.header_claims.get(JWTClaims.Public.ALGORITHM, "null")

func get_type() -> String:
return self.header_claims.get(JWTClaims.Public.TYPE, "null")

return self.header_claims.get(JWTClaims.Public.TYPE, "null")

func get_content_type() -> String:
return self.header_claims.get(JWTClaims.Public.CONTENT_TYPE, "null")

return self.header_claims.get(JWTClaims.Public.CONTENT_TYPE, "null")

func get_key_id() -> String:
return self.header_claims.get(JWTClaims.Public.KEY_ID, "null")

return self.header_claims.get(JWTClaims.Public.KEY_ID, "null")

func get_header_claim(name: String):
return self.header_claims.get(name, null)

return self.header_claims.get(name, null)

func get_header_claims() -> Dictionary:
return self.header_claims

return self.header_claims

func get_issuer() -> String:
return self.payload_claims.get(JWTClaims.Public.ISSUER, "null")

return self.payload_claims.get(JWTClaims.Public.ISSUER, "null")

func get_subject() -> String:
return self.payload_claims.get(JWTClaims.Public.SUBJECT, "null")

return self.payload_claims.get(JWTClaims.Public.SUBJECT, "null")

func get_audience() -> PackedByteArray:
return self.payload_claims.get(JWTClaims.Public.AUDIENCE, "null")

return self.payload_claims.get(JWTClaims.Public.AUDIENCE, "null")

func get_expires_at() -> int:
return self.payload_claims.get(JWTClaims.Public.EXPIRES_AT, -1)

return self.payload_claims.get(JWTClaims.Public.EXPIRES_AT, -1)

func get_not_before() -> int:
return self.payload_claims.get(JWTClaims.Public.NOT_BEFORE, -1)

return self.payload_claims.get(JWTClaims.Public.NOT_BEFORE, -1)

func get_issued_at() -> int:
return self.payload_claims.get(JWTClaims.Public.ISSUED_AT, -1)

return self.payload_claims.get(JWTClaims.Public.ISSUED_AT, -1)

func get_id() -> String:
return self.payload_claims.get(JWTClaims.Public.JWT_ID, "null")

return self.payload_claims.get(JWTClaims.Public.JWT_ID, "null")

func get_claim(name: String):
return self.payload_claims.get(name, "null")

return self.payload_claims.get(name, "null")

func get_claims() -> Dictionary:
return self.payload_claims

return self.payload_claims

func get_header() -> String:
return self.parts[0]

return self.parts[0]

func get_payload() -> String:
return self.parts[1]

return self.parts[1]

func get_signature() -> String:
return self.parts[2]

return self.parts[2]

func get_token() -> String:
return "%s.%s.%s" % parts
return ("%s.%s.%s" % parts)
34 changes: 17 additions & 17 deletions addons/jwt/src/JWTVerifier.gd
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,24 @@ enum JWTExceptions {
CLAIM_NOT_VALID
}

var jwt_decoder: JWTDecoder
var algorithm: JWTAlgorithm
var claims: Dictionary
var clock: int
var _jwt_decoder: JWTDecoder
var _algorithm: JWTAlgorithm
var _claims: Dictionary
var _clock: int
var exception: String

func _init(algorithm: JWTAlgorithm, claims: Dictionary, clock: int):
self.algorithm = algorithm
self.claims = claims
self.clock = clock
self._algorithm = algorithm
self._claims = claims
self._clock = clock

func verify_algorithm(jwt_decoder: JWTDecoder, algorithm: JWTAlgorithm) -> bool:
self.exception = "The provided Algorithm doesn't match the one defined in the JWT's Header."
return jwt_decoder.get_algorithm() == algorithm.get_name()

func verify_signature(jwt_decoder: JWTDecoder) -> bool:
self.exception = "The provided Algorithm doesn't match the one used to sign the JWT."
return algorithm.verify(jwt_decoder)
return _algorithm.verify(jwt_decoder)

func verify_claim_values(jwt_decoder: JWTDecoder, expected_claims: Dictionary) -> bool:
for claim in expected_claims.keys():
Expand Down Expand Up @@ -74,12 +74,12 @@ func assert_claim_value(jwt_decoder: JWTDecoder, claim: String) -> bool:
match typeof(jwt_decoder.get_claims().get(claim)):
TYPE_STRING: valid_value = (jwt_decoder.get_claims().get(claim) != "" and jwt_decoder.get_claims().get(claim) != "null")
TYPE_INT: valid_value = (jwt_decoder.get_claims().get(claim) >= 0)
return (jwt_decoder.get_claims().get(claim) != null and valid_value and jwt_decoder.get_claims().get(claim) == self.claims.get(claim))
return (jwt_decoder.get_claims().get(claim) != null and valid_value and jwt_decoder.get_claims().get(claim) == self._claims.get(claim))

func assert_valid_date_claim(date: int, leeway: int, should_be_future: bool) -> bool:
if date == null: return true
if should_be_future: return (self.clock - leeway) < date
else: return (self.clock + leeway) > date
if should_be_future: return (self._clock - leeway) < date
else: return (self._clock + leeway) > date

func assert_valid_header(jwt_decoder: JWTDecoder) -> bool:
self.exception = "The header is empty or invalid."
Expand All @@ -90,11 +90,11 @@ func assert_valid_payload(jwt_decoder: JWTDecoder) -> bool:
return not jwt_decoder.payload_claims.is_empty()

func verify(jwt: String) -> JWTExceptions:
self.jwt_decoder = JWTDecoder.new(jwt)
if not assert_valid_header(self.jwt_decoder): return JWTExceptions.INVALID_HEADER
if not assert_valid_payload(self.jwt_decoder): return JWTExceptions.INVALID_PAYLOAD
if not verify_algorithm(self.jwt_decoder, algorithm): return JWTExceptions.ALGORITHM_MISMATCHING
if not verify_signature(self.jwt_decoder): return JWTExceptions.INVALID_SIGNATURE
if not verify_claim_values(self.jwt_decoder, self.claims): return JWTExceptions.CLAIM_NOT_VALID
self._jwt_decoder = JWTDecoder.new(jwt)
if not assert_valid_header(self._jwt_decoder): return JWTExceptions.INVALID_HEADER
if not assert_valid_payload(self._jwt_decoder): return JWTExceptions.INVALID_PAYLOAD
if not verify_algorithm(self._jwt_decoder, _algorithm): return JWTExceptions.ALGORITHM_MISMATCHING
if not verify_signature(self._jwt_decoder): return JWTExceptions.INVALID_SIGNATURE
if not verify_claim_values(self._jwt_decoder, self._claims): return JWTExceptions.CLAIM_NOT_VALID
self.exception = ""
return JWTExceptions.OK
Loading

0 comments on commit 640ffef

Please sign in to comment.