Skip to content

Commit

Permalink
fix overflow of web_order_line_item_id in 32-bit devices (#384)
Browse files Browse the repository at this point in the history
* fixed issues on 32 bit devices where webOrderLineItemId would overflow

* added comment to explain reasoning behind arch check
  • Loading branch information
aboedo committed Nov 9, 2020
1 parent a4841e8 commit 0c7bdb8
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 21 deletions.
Expand Up @@ -42,7 +42,7 @@ struct InAppPurchase: Equatable {
let cancellationDate: Date?
let isInTrialPeriod: Bool?
let isInIntroOfferPeriod: Bool
let webOrderLineItemId: Int
let webOrderLineItemId: Int64
let promotionalOfferIdentifier: String?

var asDict: [String: Any] {
Expand All @@ -66,4 +66,4 @@ struct InAppPurchase: Equatable {
var description: String {
return String(describing: self.asDict)
}
}
}
Expand Up @@ -31,7 +31,7 @@ class InAppPurchaseBuilder {
var cancellationDate: Date?
var isInTrialPeriod: Bool?
var isInIntroOfferPeriod: Bool?
var webOrderLineItemId: Int?
var webOrderLineItemId: Int64?
var promotionalOfferIdentifier: String?

for internalContainer in container.internalContainers {
Expand All @@ -51,7 +51,7 @@ class InAppPurchaseBuilder {
case .quantity:
quantity = internalContainer.internalPayload.toInt()
case .webOrderLineItemId:
webOrderLineItemId = internalContainer.internalPayload.toInt()
webOrderLineItemId = internalContainer.internalPayload.toInt64()
case .productType:
productType = InAppPurchaseProductType(rawValue: internalContainer.internalPayload.toInt())
case .isInIntroOfferPeriod:
Expand Down
Expand Up @@ -6,12 +6,12 @@
import Foundation

extension ArraySlice where Element == UInt8 {
func toUInt() -> UInt {
func toUInt() -> UInt64 {
let array = Array(self)
var result: UInt = 0
var result: UInt64 = 0
for idx in 0..<(array.count) {
let shiftAmount = UInt((array.count) - idx - 1) * 8
result += UInt(array[idx]) << shiftAmount
result += UInt64(array[idx]) << shiftAmount
}
return result
}
Expand All @@ -20,6 +20,10 @@ extension ArraySlice where Element == UInt8 {
return Int(self.toUInt())
}

func toInt64() -> Int64 {
return Int64(self.toUInt())
}

func toBool() -> Bool {
return self.toUInt() == 1
}
Expand Down
Expand Up @@ -92,7 +92,7 @@ class AppleReceiptBuilderTests: XCTestCase {
cancellationDate: nil,
isInTrialPeriod: false,
isInIntroOfferPeriod: false,
webOrderLineItemId: 658464,
webOrderLineItemId: Int64(658464),
promotionalOfferIdentifier: nil)

let receipt = try! self.appleReceiptBuilder.build(fromContainer: receiptContainer)
Expand Down
Expand Up @@ -21,7 +21,7 @@ class InAppPurchaseBuilderTests: XCTestCase {
let cancellationDate = Date.from(year: 2019, month: 7, day: 4, hour: 7, minute: 1, second: 45)
let isInTrialPeriod = false
let isInIntroOfferPeriod = true
let webOrderLineItemId = 897501072
let webOrderLineItemId = Int64(897501072)
let promotionalOfferIdentifier = "com.revenuecat.productPromoOffer"

private let containerFactory = ContainerFactory()
Expand Down
Expand Up @@ -17,8 +17,12 @@ class UInt8ExtensionsTests: XCTestCase {
}

func testBitAtIndexRaisesIfInvalidIndex() {
// throwAssertion isn't supported on 32-bit simulators
// https://github.com/Quick/Nimble/blob/master/Sources/Nimble/Matchers/ThrowAssertion.swift#L46
#if arch(x86_64)
expect { _ = UInt8(0b1).bitAtIndex(7) }.notTo(throwAssertion())
expect { _ = UInt8(0b1).bitAtIndex(8) }.to(throwAssertion())
#endif
}

func testValueInRangeGetsCorrectValue() {
Expand All @@ -32,8 +36,12 @@ class UInt8ExtensionsTests: XCTestCase {
}

func testValueInRangeRaisesIfInvalidRange() {
// throwAssertion isn't supported on 32-bit simulators
// https://github.com/Quick/Nimble/blob/master/Sources/Nimble/Matchers/ThrowAssertion.swift#L46
#if arch(x86_64)
expect{ _ = UInt8(0b10000010).valueInRange(from: 1, to: 6)}.notTo(throwAssertion())
expect{ _ = UInt8(0b10000010).valueInRange(from: 6, to: 1)}.to(throwAssertion())
expect{ _ = UInt8(0b10000010).valueInRange(from: 6, to: 8)}.to(throwAssertion())
#endif
}
}
Expand Up @@ -48,7 +48,7 @@ class ReceiptParsingRealReceiptTests: XCTestCase {
expect(inAppPurchase0.cancellationDate).to(beNil())
expect(inAppPurchase0.isInTrialPeriod) == false
expect(inAppPurchase0.isInIntroOfferPeriod) == false
expect(inAppPurchase0.webOrderLineItemId) == 1000000054042695
expect(inAppPurchase0.webOrderLineItemId) == Int64(1000000054042695)
expect(inAppPurchase0.promotionalOfferIdentifier).to(beNil())

let inAppPurchase1 = inAppPurchases[1]
Expand All @@ -63,7 +63,7 @@ class ReceiptParsingRealReceiptTests: XCTestCase {
expect(inAppPurchase1.cancellationDate).to(beNil())
expect(inAppPurchase1.isInTrialPeriod) == false
expect(inAppPurchase1.isInIntroOfferPeriod) == false
expect(inAppPurchase1.webOrderLineItemId) == 1000000054042739
expect(inAppPurchase1.webOrderLineItemId) == Int64(1000000054042739)
expect(inAppPurchase1.promotionalOfferIdentifier).to(beNil())

let inAppPurchase2 = inAppPurchases[2]
Expand All @@ -78,7 +78,7 @@ class ReceiptParsingRealReceiptTests: XCTestCase {
expect(inAppPurchase2.cancellationDate).to(beNil())
expect(inAppPurchase2.isInTrialPeriod) == false
expect(inAppPurchase2.isInIntroOfferPeriod) == false
expect(inAppPurchase2.webOrderLineItemId) == 1000000054044460
expect(inAppPurchase2.webOrderLineItemId) == Int64(1000000054044460)
expect(inAppPurchase2.promotionalOfferIdentifier).to(beNil())

let inAppPurchase3 = inAppPurchases[3]
Expand All @@ -93,7 +93,7 @@ class ReceiptParsingRealReceiptTests: XCTestCase {
expect(inAppPurchase3.cancellationDate).to(beNil())
expect(inAppPurchase3.isInTrialPeriod) == false
expect(inAppPurchase3.isInIntroOfferPeriod) == false
expect(inAppPurchase3.webOrderLineItemId) == 1000000054044520
expect(inAppPurchase3.webOrderLineItemId) == Int64(1000000054044520)
expect(inAppPurchase3.promotionalOfferIdentifier).to(beNil())

let inAppPurchase4 = inAppPurchases[4]
Expand All @@ -108,7 +108,7 @@ class ReceiptParsingRealReceiptTests: XCTestCase {
expect(inAppPurchase4.cancellationDate).to(beNil())
expect(inAppPurchase4.isInTrialPeriod) == false
expect(inAppPurchase4.isInIntroOfferPeriod) == false
expect(inAppPurchase4.webOrderLineItemId) == 1000000054044587
expect(inAppPurchase4.webOrderLineItemId) == Int64(1000000054044587)
expect(inAppPurchase4.promotionalOfferIdentifier).to(beNil())

let inAppPurchase5 = inAppPurchases[5]
Expand All @@ -123,7 +123,7 @@ class ReceiptParsingRealReceiptTests: XCTestCase {
expect(inAppPurchase5.cancellationDate).to(beNil())
expect(inAppPurchase5.isInTrialPeriod) == false
expect(inAppPurchase5.isInIntroOfferPeriod) == false
expect(inAppPurchase5.webOrderLineItemId) == 1000000054044637
expect(inAppPurchase5.webOrderLineItemId) == Int64(1000000054044637)
expect(inAppPurchase5.promotionalOfferIdentifier).to(beNil())

let inAppPurchase6 = inAppPurchases[6]
Expand All @@ -138,7 +138,7 @@ class ReceiptParsingRealReceiptTests: XCTestCase {
expect(inAppPurchase6.cancellationDate).to(beNil())
expect(inAppPurchase6.isInTrialPeriod) == false
expect(inAppPurchase6.isInIntroOfferPeriod) == false
expect(inAppPurchase6.webOrderLineItemId) == 1000000054044710
expect(inAppPurchase6.webOrderLineItemId) == Int64(1000000054044710)
expect(inAppPurchase6.promotionalOfferIdentifier).to(beNil())

let inAppPurchase7 = inAppPurchases[7]
Expand All @@ -153,7 +153,7 @@ class ReceiptParsingRealReceiptTests: XCTestCase {
expect(inAppPurchase7.cancellationDate).to(beNil())
expect(inAppPurchase7.isInTrialPeriod) == false
expect(inAppPurchase7.isInIntroOfferPeriod) == false
expect(inAppPurchase7.webOrderLineItemId) == 1000000054044800
expect(inAppPurchase7.webOrderLineItemId) == Int64(1000000054044800)
expect(inAppPurchase7.promotionalOfferIdentifier).to(beNil())

let inAppPurchase8 = inAppPurchases[8]
Expand All @@ -168,7 +168,7 @@ class ReceiptParsingRealReceiptTests: XCTestCase {
expect(inAppPurchase8.cancellationDate).to(beNil())
expect(inAppPurchase8.isInTrialPeriod) == true
expect(inAppPurchase8.isInIntroOfferPeriod) == false
expect(inAppPurchase8.webOrderLineItemId) == 1000000054042694
expect(inAppPurchase8.webOrderLineItemId) == Int64(1000000054042694)
expect(inAppPurchase8.promotionalOfferIdentifier).to(beNil())
}
}
Expand Down
26 changes: 26 additions & 0 deletions PurchasesCoreSwiftTests/TestHelpers/ContainerFactory.swift
Expand Up @@ -68,6 +68,18 @@ class ContainerFactory {
internalContainers: [])
}

func int64Container(int64: Int64) -> ASN1Container {
let intAsBytes = int64ToBytes(int: int64)
let bytesUsedForLength = intAsBytes.count < 128 ? 1 : intToBytes(int: intAsBytes.count).count + 1

return ASN1Container(containerClass: .application,
containerIdentifier: .octetString,
encodingType: .primitive,
length: ASN1Length(value: intAsBytes.count, bytesUsedForLength: bytesUsedForLength),
internalPayload: ArraySlice(intAsBytes),
internalContainers: [])
}

func constructedContainer(containers: [ASN1Container],
encodingType: ASN1EncodingType = .constructed) -> ASN1Container {
let payload = containers.flatMap { self.headerBytes(forContainer: $0) + $0.internalPayload }
Expand Down Expand Up @@ -96,6 +108,14 @@ class ContainerFactory {
return constructedContainer(containers: [typeContainer, versionContainer, valueContainer])
}

func receiptAttributeContainer(attributeType: BuildableReceiptAttributeType, _ value: Int64) -> ASN1Container {
let typeContainer = intContainer(int: attributeType.rawValue)
let versionContainer = intContainer(int: 1)
let valueContainer = constructedContainer(containers: [int64Container(int64: value)])

return constructedContainer(containers: [typeContainer, versionContainer, valueContainer])
}

func receiptAttributeContainer(attributeType: BuildableReceiptAttributeType, _ date: Date) -> ASN1Container {
let typeContainer = intContainer(int: attributeType.rawValue)
let versionContainer = intContainer(int: 1)
Expand Down Expand Up @@ -155,6 +175,12 @@ private extension ContainerFactory {
return arrayWithoutInsignificantBytes
}

func int64ToBytes(int: Int64) -> [UInt8] {
let intAsBytes = withUnsafeBytes(of: int.bigEndian, Array.init)
let arrayWithoutInsignificantBytes = Array(intAsBytes.drop(while: { $0 == 0 }))
return arrayWithoutInsignificantBytes
}

func headerBytes(forContainer container: ASN1Container) -> [UInt8] {
let identifierHeader = (container.containerClass.rawValue << 6
| container.encodingType.rawValue << 5
Expand Down
2 changes: 1 addition & 1 deletion PurchasesTests/Networking/BackendTests.swift
Expand Up @@ -1112,7 +1112,7 @@ class BackendTests: XCTestCase {
"signature_data": [
"signature": "Base64 encoded signature",
"nonce": "A UUID",
"timestamp": 123413232131
"timestamp": Int64(123413232131)
],
"signature_error": nil
]
Expand Down
2 changes: 1 addition & 1 deletion PurchasesTests/Purchasing/PurchaserInfoTests.swift
Expand Up @@ -23,7 +23,7 @@ class EmptyPurchaserInfoTests: XCTestCase {
class BasicPurchaserInfoTests: XCTestCase {
let validSubscriberResponse = [
"request_date": "2018-10-19T02:40:36Z",
"request_date_ms": 1563379533946,
"request_date_ms": Int64(1563379533946),
"subscriber": [
"original_app_user_id": "app_user_id",
"original_application_version": "2083",
Expand Down
2 changes: 1 addition & 1 deletion PurchasesTests/Purchasing/PurchasesTests.swift
Expand Up @@ -1812,7 +1812,7 @@ class PurchasesTests: XCTestCase {
if #available(iOS 12.2, *) {
setupPurchases()
let product = MockSKProduct(mockProductIdentifier: "com.product.id1")
let discount = SKPaymentDiscount.init(identifier: "discount", keyIdentifier: "TIKAMASALA1", nonce: UUID(), signature: "Base64 encoded signature", timestamp: 123413232131)
let discount = SKPaymentDiscount.init(identifier: "discount", keyIdentifier: "TIKAMASALA1", nonce: UUID(), signature: "Base64 encoded signature", timestamp: NSNumber(value: Int64(123413232131)))

self.purchases?.purchaseProduct(product, discount: discount) { (tx, info, error, userCancelled) in

Expand Down

0 comments on commit 0c7bdb8

Please sign in to comment.