Skip to content

Commit

Permalink
Update JWT plugin code again and adapt to new usage.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisl8 committed Dec 13, 2023
1 parent ccc7b3b commit 8f149a3
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 192 deletions.
150 changes: 76 additions & 74 deletions addons/jwt/src/JWTAlgorithm.gd
Original file line number Diff line number Diff line change
@@ -1,81 +1,83 @@
class_name JWTAlgorithm
extends RefCounted

enum Type { HMAC1, HMAC256, RSA256 }

var _alg: int = -1
var _secret: String = ""
func get_name() -> String:
assert(false, "JWTAlgorithm subclasses must implement `get_name()`")
return ""

var crypto: Crypto = Crypto.new()
var _public_crypto: CryptoKey = CryptoKey.new()
var _private_crypto: CryptoKey = CryptoKey.new()

func sign(_text: String) -> PackedByteArray:
assert(false, "JWTAlgorithm subclasses must implement `sign()`")
return []

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 ""


func _digest(ctx_type: HashingContext.HashType, data: PackedByteArray) -> PackedByteArray:
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


# 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)

func verify(_jwt: JWTDecoder) -> bool:
assert(false, "JWTAlgorithm subclasses must implement `verify()`")
return false


# TODO: HSA1 is not secure and should be removed
class HSA1:
extends JWTAlgorithm
var _secret: PackedByteArray

func _init(secret: String):
push_warning("HSA1 is not secure, and should not be used")
self._secret = secret.to_utf8_buffer()

func get_name() -> String:
return "HSA1"

func sign(text: String) -> PackedByteArray:
var crypto := Crypto.new()
return crypto.hmac_digest(HashingContext.HASH_SHA1, self._secret, text.to_utf8_buffer())

func verify(jwt: JWTDecoder) -> bool:
var payload: String = jwt.get_header() + "." + jwt.get_payload()
var signature_bytes := self.sign(payload)
return jwt.get_signature() == JWTUtils.urlsafe_b64encode(signature_bytes)


class HS256:
extends JWTAlgorithm
var _secret: PackedByteArray

func get_name() -> String:
return "HS256"

func _init(secret: String):
self._secret = secret.to_utf8_buffer()

func sign(text: String) -> PackedByteArray:
var crypto := Crypto.new()
return crypto.hmac_digest(HashingContext.HASH_SHA256, self._secret, text.to_utf8_buffer())

func verify(jwt: JWTDecoder) -> bool:
var payload: String = jwt.get_header() + "." + jwt.get_payload()
var signature_bytes := self.sign(payload)
return jwt.get_signature() == JWTUtils.urlsafe_b64encode(signature_bytes)


class RS256:
extends JWTAlgorithm

var _public_key: CryptoKey
var _private_key: CryptoKey

func _init(public_key: CryptoKey, private_key := CryptoKey.new()):
self._public_key = public_key
self._private_key = private_key

func get_name() -> String:
return "RS256"

func sign(text: String) -> PackedByteArray:
var crypto := Crypto.new()
return crypto.sign(HashingContext.HASH_SHA256, text.sha256_buffer(), self._private_key)

func verify(jwt: JWTDecoder) -> bool:
var crypto := Crypto.new()
var payload: PackedByteArray = (jwt.get_header() + "." + jwt.get_payload()).sha256_buffer()
var signature: PackedByteArray = JWTUtils.urlsafe_b64decode(jwt.get_signature())
return crypto.verify(HashingContext.HASH_SHA256, payload, signature, self._public_key)
48 changes: 0 additions & 48 deletions addons/jwt/src/JWTAlgorithmBuilder.gd

This file was deleted.

10 changes: 5 additions & 5 deletions addons/jwt/src/JWTBaseBuilder.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ extends RefCounted


func with_header(header_claims: Dictionary) -> JWTBaseBuilder:
self.header_claims = header_claims
self._header_claims = header_claims
return self


func with_algorithm(algorithm: String) -> JWTBaseBuilder:
self.header_claims[JWTClaims.Public.ALGORITHM] = algorithm
self._header_claims[JWTClaims.Public.ALGORITHM] = algorithm
return self


func with_type(type: String) -> JWTBaseBuilder:
self.header_claims[JWTClaims.Public.TYPE] = type
self._header_claims[JWTClaims.Public.TYPE] = type
return self


func with_key_id(key_id: String) -> JWTBaseBuilder:
self.header_claims[JWTClaims.Public.KEY_ID] = key_id
self._header_claims[JWTClaims.Public.KEY_ID] = key_id
return self


Expand Down Expand Up @@ -56,7 +56,7 @@ func with_issued_at(issued_at: int) -> JWTBaseBuilder:


func with_jwt_id(jwt_id: String) -> JWTBaseBuilder:
self.header_claims[JWTClaims.Public.JWT_ID] = jwt_id
self._header_claims[JWTClaims.Public.JWT_ID] = jwt_id
return self


Expand Down
29 changes: 14 additions & 15 deletions addons/jwt/src/JWTBuilder.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ extends JWTBaseBuilder
var crypto: Crypto = Crypto.new()

var _algorithm: JWTAlgorithm
var header_claims: Dictionary = {alg = "", typ = "JWT"}
var payload_claims: Dictionary
var secret: String
var _header_claims: Dictionary = {alg = "", typ = "JWT"}
var _payload_claims: Dictionary


func _init(
Expand All @@ -15,42 +14,42 @@ func _init(
payload_claims_param: Dictionary = {}
):
if not header_claims_param.is_empty():
self.header_claims = header_claims_param
self._header_claims = header_claims_param
if not payload_claims_param.is_empty():
self.payload_claims = payload_claims_param
self._payload_claims = payload_claims_param
if algorithm_param != null:
self._algorithm = algorithm_param
self.header_claims.alg = self._algorithm.get_name()
self._header_claims.alg = self._algorithm.get_name()


func add_claim(name: String, value) -> void:
match typeof(value):
TYPE_ARRAY, TYPE_PACKED_STRING_ARRAY:
if value.size() == 0:
self.payload_claims.erase(name)
self._payload_claims.erase(name)
return
TYPE_STRING:
if value.length() == 0:
self.payload_claims.erase(name)
self._payload_claims.erase(name)
return
_:
if value == null:
self.payload_claims.erase(name)
self._payload_claims.erase(name)
return
self.payload_claims[name] = value
self._payload_claims[name] = value


func sign(algorithm: JWTAlgorithm = null) -> String:
if algorithm != null:
self._algorithm = algorithm
assert(algorithm != null, "Can't sign a JWT without an Algorithm")
with_algorithm(algorithm.get_name())
var header: String = JWTUtils.base64URL_encode(
JSON.stringify(self.header_claims).to_utf8_buffer()
var header: String = JWTUtils.urlsafe_b64encode(
JSON.stringify(self._header_claims).to_utf8_buffer()
)
var payload: String = JWTUtils.base64URL_encode(
JSON.stringify(self.payload_claims).to_utf8_buffer()
var payload: String = JWTUtils.urlsafe_b64encode(
JSON.stringify(self._payload_claims).to_utf8_buffer()
)
var signature_bytes: PackedByteArray = algorithm.sign(header + "." + payload)
var signature: String = JWTUtils.base64URL_encode(signature_bytes)
var signature: String = JWTUtils.urlsafe_b64encode(signature_bytes)
return "%s.%s.%s" % [header, payload, signature]
Loading

0 comments on commit 8f149a3

Please sign in to comment.