Skip to content

Commit

Permalink
if loads() fails, the properties are not loaded
Browse files Browse the repository at this point in the history
  • Loading branch information
stannie committed Apr 11, 2015
1 parent e156a10 commit 799e741
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 41 deletions.
28 changes: 13 additions & 15 deletions JWT/JWTTests/JWTTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,16 @@ class JWTTests: XCTestCase {
func test_random_string_as_JWT() {
var error: NSError?
var jwt = JWT(algorithms: ["HS512","RS512"])
XCTAssert(jwt.loads("randomstring", key: nil, verify: false, error: &error) == false, "random string should not load")
XCTAssert(jwt.loads("randomstring", verify: false, error: &error) == false, "random string should not load")
XCTAssert(jwt.body.count == 0, "loading garbage should leave body empty")
}

func test_alg_none_with_sig() {
let jwt_none_sig = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.qrq-939iZydNFdNsTosbSteghjc2VcK9EZVklxfQgiU"
var jwt = JWT(algorithms: ["none","HS512","RS512"])
XCTAssert(jwt.loads(jwt_none_sig, key: nil, verify: true) == false, "alg=none with a signature is not valid")
XCTAssert(jwt.loads(jwt_none_sig, verify: true) == false, "alg=none with a signature is not valid")
jwt = JWT(algorithms: ["HS512","RS512"])
XCTAssert(jwt.loads(jwt_none_sig, key: nil, verify: false) == false, "none is not on whitelist")
XCTAssert(jwt.loads(jwt_none_sig, verify: false) == false, "none is not on whitelist")
}

func test_timestamps() {
Expand All @@ -74,22 +74,22 @@ class JWTTests: XCTestCase {
var s = ""
jwt_dated.body["exp"] = now-100
s = jwt_dated.dumps()!
XCTAssert(jwt.loads(s, key: nil, verify: true) == false, "exp in past \(s)")
XCTAssert(jwt.loads(s, verify: true) == false, "exp in past \(s)")
jwt_dated.body["exp"] = now+100 // and leave it there for next tests
s = jwt_dated.dumps()!
XCTAssert(jwt.loads(s, key: nil, verify: true) == true, "exp in future \(s)")
XCTAssert(jwt.loads(s, verify: true) == true, "exp in future \(s)")
jwt_dated.body["nbf"] = now+100
s = jwt_dated.dumps()!
XCTAssert(jwt.loads(s, key: nil, verify: true) == false, "nbf in future \(s)")
XCTAssert(jwt.loads(s, verify: true) == false, "nbf in future \(s)")
jwt_dated.body["nbf"] = now-100 // and leave it there for next tests
s = jwt_dated.dumps()!
XCTAssert(jwt.loads(s, key: nil, verify: true) == true, "nbf in past \(s)")
XCTAssert(jwt.loads(s, verify: true) == true, "nbf in past \(s)")
jwt_dated.body["iat"] = now+100
s = jwt_dated.dumps()!
XCTAssert(jwt.loads(s, key: nil, verify: true) == false, "iat in future \(s)")
XCTAssert(jwt.loads(s, verify: true) == false, "iat in future \(s)")
jwt_dated.body["iat"] = now-100 // and leave it there for next tests
s = jwt_dated.dumps()!
XCTAssert(jwt.loads(s, key: nil, verify: true) == true, "iat in past \(s)")
XCTAssert(jwt.loads(s, verify: true) == true, "iat in past \(s)")
}

func test_NaCl_JWT_load_dump_load() {
Expand All @@ -103,12 +103,10 @@ class JWTTests: XCTestCase {
XCTAssert(kp != nil, "Key pair generation")
var jwt = JWTNaCl(algorithms: ["Ed25519"])
XCTAssert(jwt.loads(jwt_ed, key: kp!.publicKey, verify: true) == false, "NaCl JWT should not validate with wrong key")
// but is still loaded (DO WE WANT THAT?)
// but is still loaded (DO WE WANT THAT?) NOW FAILS
jwt = JWTNaCl(header: ["alg":"Ed25519","kid":"XN7VpEX1uCxxhvwUuacYhuU9t6uxgLahRiLeSEHENik"], body: ["hello":"world"], algorithms: ["Ed25519"])
let jwt_str = jwt.dumps(key: kp!.secretKey)! // valid Ed25519 signed token
jwt.header = [:]
XCTAssert(jwt.header["alg"] as? String == "none", "after reset of header, alg should be none")
jwt.body = [:]
XCTAssert(jwt.loads(jwt_str, key: nil, verify: true) == false, "verify a generated JWT to kid when signed with fresh key")
XCTAssert(jwt.loads(jwt_str, verify: true) == false, "verify a generated JWT with wrong kid when signed with fresh key")
XCTAssert(jwt.loads(jwt_str, key: kp!.publicKey, verify: true), "verify a generated JWT with its public key")
}

Expand All @@ -117,7 +115,7 @@ class JWTTests: XCTestCase {
let jwt_str = "eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiSldUIiwia2lkIjoiYUJHb3dQSGNJdHBvdmVWenJyUXNTbms2NWNfcWhLdmZqZC00d3lQVWZVUSJ9.eyJwaG9uZV9udW1iZXIiOiIrMzA2OTQ3ODk4NjA1Iiwic2NvcGUiOiJwaG9uZSIsImF1ZCI6Imh0dHBzOlwvXC81LWRvdC1hdXRoZW50aXFpby5hcHBzcG90LmNvbSIsInN1YiI6ImFCR293UEhjSXRwb3ZlVnpyclFzU25rNjVjX3FoS3ZmamQtNHd5UFVmVVEiLCJ0eXBlIjoibW9iaWxlIn0.kD4YcuAb7v3cxlRZTrUbew1lWiY3G8uEmRguizy1KJs"
var jwt = JWTNaCl(algorithms: ["none","HS512","RS512"])
self.measureBlock() {
let ok = jwt.loads(jwt_str, key: nil, verify: true)
let ok = jwt.loads(jwt_str, verify: true)
}
}

Expand Down
63 changes: 37 additions & 26 deletions SwiftJWT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,16 @@ public class JWT {

public func loads(jwt: String, key: NSData? = nil, verify: Bool = true, error: NSErrorPointer = nil) -> Bool {
// load a JWT string into this object
var sig = ""
var hdr: [String: AnyObject]?
var payload: [String: AnyObject]?
var algorithm: String?

// clear object properties
self.header = [:]
self.body = [:]
var sig = ""

// split into parts, header, body, optional signature
// split JWT string into parts: header, body, optional signature
let parts: [String] = jwt.componentsSeparatedByString(".")
switch parts.count {
case 2: break
Expand All @@ -61,45 +66,48 @@ public class JWT {

// decode the header (a URL-safe, base 64 encoded JSON dict) from 1st part
let hdr_data = parts[0].base64SafeUrlDecode()
if let dictionary = NSJSONSerialization.JSONObjectWithData(hdr_data, options: NSJSONReadingOptions(0), error: error) as? [String: AnyObject] {
self.header = dictionary
let alg = self.header["alg"] as? String
if !self.implemented(alg) {
hdr = NSJSONSerialization.JSONObjectWithData(hdr_data, options: NSJSONReadingOptions(0), error: error) as? [String: AnyObject]
if hdr != nil {
// check that "alg" header is on whitelist (and thus implemented) ; even if verify == false
algorithm = hdr!["alg"] as? String
if !self.whitelisted(algorithm) {
return false // TODO: populate NSError
}
}
else {
return false // TODO: populate NSError
}
// check that "alg" header is on whitelist (and thus implemented) ; even if verify == false
let algorithm = header["alg"] as? String
if !self.whitelisted(algorithm) {
return false // TODO: populate NSError
}
// decode the body (a URL-safe base 64 encoded JSON dict) from the 2nd part
if parts.count > 1 {
let body_data = parts[1].base64SafeUrlDecode()
if let dictionary = NSJSONSerialization.JSONObjectWithData(body_data, options: NSJSONReadingOptions(0), error: error) as? [String: AnyObject] {
self.body = dictionary
payload = NSJSONSerialization.JSONObjectWithData(body_data, options: NSJSONReadingOptions(0), error: error) as? [String: AnyObject]
if payload == nil {
return false // TODO: populate NSError
}
}
else {
return false // TODO: populate NSError
}

// all went well so far, so let's set the object properties
// TODO: set properties even later (but are needed by verification methods now)
self.header = hdr!
self.body = payload!

if verify {
// verify the signature, a URL-safe base64 encoded string
let hdr_body: String = parts[0] + "." + parts[1] // header & body of a JWT
let data = hdr_body.dataUsingEncoding(NSUTF8StringEncoding)!
if self.verify_signature(data, signature: sig, algorithm: algorithm!, key: key) == false {
self.header = [:]; self.body = [:] // reset
return false // TODO: populate NSError
}
// verify content fields
if self.verify_content() == false {
return false
self.header = [:]; self.body = [:] // reset
return false // TODO: populate NSError
}
}
// TODO: do not load header & body if verification fails?
return true
}

Expand Down Expand Up @@ -162,6 +170,7 @@ public class JWT {

func implemented(algorithm: String?) -> Bool {
let algorithms = ["none", "HS256", "HS384", "HS512"]
// TODO: add RS256, RS384, RS512 when rsa_* methods below are done
for alg in algorithms {
if alg == algorithm {
return true
Expand Down Expand Up @@ -445,15 +454,16 @@ extension NSData {
let privkey: SecKey? = nil
let msg = UnsafePointer<UInt8>(self.bytes)
let msglen = UInt(self.length)
let digestLen = algorithm.digestLength()

let sha_buf = UnsafeMutablePointer<UInt8>.alloc(Int(CC_SHA256_DIGEST_LENGTH)) // or 224 or 384 or 512
let sha_result = CC_SHA256(msg, CC_LONG(msglen), sha_buf) // or 224 or 384 or 512
let sha_buf = UnsafeMutablePointer<UInt8>.alloc(Int(digestLen))
let sha_result = CC_SHA256(msg, CC_LONG(msglen), sha_buf) // TODO: use 384 or 512 versions, depending on algorithm

var sig = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))! // or 224 or 384 or 512
var sigbuf = UnsafeMutablePointer<UInt8>(sig.mutableBytes) // or UnsafeMutablePointer<UInt8>.alloc(Int(CC_SHA256_DIGEST_LENGTH)) ?
let siglen = UnsafeMutablePointer<UInt>.alloc(1) // correct? and initialize to CC_SHA256_DIGEST_LENGTH ?
var sig = NSMutableData(length: Int(digestLen))!
var sigbuf = UnsafeMutablePointer<UInt8>(sig.mutableBytes) // or UnsafeMutablePointer<UInt8>.alloc(Int(digestLen)) ?
let siglen = UnsafeMutablePointer<UInt>.alloc(1) // correct? and initialize to digestLen ?

// TODO: fix error in next line, call to SecKeyRawSign
// TODO: fix error in next line, call to SecKeyRawSign, and use right padding constant
//let status = SecKeyRawSign(key: privkey!, padding: kSecPaddingPKCS1SHA256, dataToSign: msg, dataToSignLen: msglen, sig: sigbuf, sigLen: siglen)

//OSStatus SecKeyRawSign(
Expand All @@ -471,16 +481,17 @@ extension NSData {
let pubkey: SecKey? = nil
let msg = UnsafePointer<UInt8>(self.bytes)
let msglen = UInt(self.length)
let digestLen = algorithm.digestLength()

let sha_buf = UnsafeMutablePointer<UInt8>.alloc(Int(CC_SHA256_DIGEST_LENGTH))
let sha_result = CC_SHA256(msg, CC_LONG(msglen), sha_buf)
let sha_buf = UnsafeMutablePointer<UInt8>.alloc(Int(digestLen))
let sha_result = CC_SHA256(msg, CC_LONG(msglen), sha_buf) // TODO: use 384 or 512 versions, depending on algorithm

var sig = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))!
var sig = NSMutableData(length: Int(digestLen))!
let sig_raw = signature.base64SafeUrlDecode()
var sigbuf = UnsafeMutablePointer<UInt8>(sig_raw.bytes) // or UnsafeMutablePointer<UInt8>.alloc(Int(CC_SHA256_DIGEST_LENGTH)) ?
var sigbuf = UnsafeMutablePointer<UInt8>(sig_raw.bytes) // or UnsafeMutablePointer<UInt8>.alloc(Int(digestLen)) ?
let siglen = UInt(sig_raw.length)

// TODO: fix error in next line, call to SecKeyRawSign
// TODO: fix error in next line, call to SecKeyRawVerify, and use right padding constant
// let status = SecKeyRawVerify(key: pubkey!, padding: kSecPaddingPKCS1SHA256, signedData: msg, signedDataLen: msglen, sig: sigbuf, sigLen: siglen)
// OSStatus SecKeyRawVerify(key: SecKey!, padding: SecPadding, signedData: UnsafePointer<UInt8>, signedDataLen: UInt, sig: UnsafePointer<UInt8>, sigLen: UInt)

Expand Down

0 comments on commit 799e741

Please sign in to comment.