Giter Club home page Giter Club logo

swiftylocalreceiptvalidator's Introduction

Swifty Local Receipt Validator

This repository contains an example implementation of local receipt validation logic for iOS in Swift.

Prerequisites

  1. You need a copy of Apple's Root Certificate included in your application bundle for local receipt validation to succeed. I wrote "Receipt Validation โ€“ Verifying a Receipt Signature in Swift" to guide you through this process if you need help.
  2. You need OpenSSL to be statically-linked to your project. I wrote "OpenSSL for iOS & Swift the Easy Way" to guide you through this process if you need help.
  3. You need to include the following additional resources after OpenSSL is installed. Example implementations are provided in the demo project.

Disclaimer

Preventing software piracy is hard. The code presented in this repository is not meant to protect you against unauthorized usage of your app or its features. This code is meant to be used for learning purposes only. If you use this code in your app, you do it at your own risk.

You must take additional efforts to obfuscate the code presented here to thwart an attacker's attempt at circumventing the receipt validation logic contained within this repository.

Usage

Output Types

In order to make sense of the call site, I thought it might be helpful to include the output that you can expect from the ReceiptValidator:

enum ReceiptValidationResult {
	case success(ParsedReceipt)
	case error(ReceiptValidationError)
}

enum ReceiptValidationError : Error {
	case couldNotFindReceipt
	case emptyReceiptContents
	case receiptNotSigned
	case appleRootCertificateNotFound
	case receiptSignatureInvalid
	case malformedReceipt
	case malformedInAppPurchaseReceipt
	case incorrectHash
}

struct ParsedReceipt {
	let bundleIdentifier: String?
	let bundleIdData: NSData?
	let appVersion: String?
	let opaqueValue: NSData?
	let sha1Hash: NSData?
	let inAppPurchaseReceipts: [ParsedInAppPurchaseReceipt]?
	let originalAppVersion: String?
	let receiptCreationDate: Date?
	let expirationDate: Date?
}

struct ParsedInAppPurchaseReceipt {
	let quantity: Int?
	let productIdentifier: String?
	let transactionIdentifier: String?
	let originalTransactionIdentifier: String?
	let purchaseDate: Date?
	let originalPurchaseDate: Date?
	let subscriptionExpirationDate: Date?
	let cancellationDate: Date?
	let webOrderLineItemId: Int?
}

Call Site

let receiptValidator = ReceiptValidator()
let validationResult = receiptValidator.validateReceipt()
		
switch validationResult {
case .success(let receipt):
  // Work with parsed receipt data. Possibilities might be...
    // enable a feature of your app
    // remove ads
    // etc...
case .error(let error):
  // Handle receipt validation failure. Possibilities might be...
    // use StoreKit to request a new receipt
    // enter a "grace period"
    // disable a feature of your app
    // etc...
}

Explanatory Guides

Throughout the development of the code in this repository, I wrote up several guides at https://www.andrewcbancroft.com to explain what each step along the way in the receipt validation process is doing. If you'd like to understand more about what's going on under the hood, you can read up on any step below:

Implementing In-app Purchases on iOS

Learning about in-app purchases on iOS?

I am the author of Implementing In-app Purchases on iOS at Pluralsight.

In the course, you'll learn to offer digital products as in-app purchases from end to end:

โ‡๏ธ Configure products in App Store Connect & Xcode

โ‡๏ธ Build and test a fully-working Store view

โ‡๏ธ Protect your revenue by validating App Store receipts (I teach server-side validation in the course)

โ‡๏ธ Unlock content that users have legitimately purchased

swiftylocalreceiptvalidator's People

Contributors

andrewcbancroft avatar yangyubo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftylocalreceiptvalidator's Issues

Rare Crash in ReceiptValidator.deviceIdentifierData

Here's a rare crash:

Exception Type:        EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes:       0x0000000000000001, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Signal:    Illegal instruction: 4
Termination Reason:    Namespace SIGNAL, Code 0x4
Terminating Process:   exc handler [12610]

Thread 0 Crashed:
0   ...      	0x000000010083b5e6 ReceiptValidator.deviceIdentifierData() (in ...) (ReceiptValidator.swift:138)
1   ...      	0x000000010083b00a ReceiptValidator.validateHash(receipt:) (in ...) (ReceiptValidator.swift:168)
2   ...      	0x0000000100839c1b ReceiptValidator.validateReceipt() (in ...) (ReceiptValidator.swift:99)
3   ...      	...
4   ...      	...
5   ...      	...
6   com.apple.Foundation          	0x00007fff30658646 __NSFireTimer + 80
7   com.apple.CoreFoundation      	0x00007fff2e2c7dfd __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
8   com.apple.CoreFoundation      	0x00007fff2e2c79b0 __CFRunLoopDoTimer + 859
9   com.apple.CoreFoundation      	0x00007fff2e2c74f0 __CFRunLoopDoTimers + 333
10  com.apple.CoreFoundation      	0x00007fff2e2a86fe __CFRunLoopRun + 2119
11  com.apple.CoreFoundation      	0x00007fff2e2a7c64 CFRunLoopRunSpecific + 463
12  com.apple.HIToolbox           	0x00007fff2d53eab5 RunCurrentEventLoopInMode + 293
13  com.apple.HIToolbox           	0x00007fff2d53e7eb ReceiveNextEventCommon + 618
14  com.apple.HIToolbox           	0x00007fff2d53e568 _BlockUntilNextEventMatchingListInModeWithFilter + 64
15  com.apple.AppKit              	0x00007fff2b7f9363 _DPSNextEvent + 997
16  com.apple.AppKit              	0x00007fff2b7f8102 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1362
17  com.apple.AppKit              	0x00007fff2b7f2165 -[NSApplication run] + 699
18  com.apple.AppKit              	0x00007fff2b7e18a3 NSApplicationMain + 780
19  ...      	0x00000001008201d9 main (in ...) (AppDelegate.swift:15)
20  libdyld.dylib                 	0x00007fff5b4f5ed9 start + 1

ReceiptValidator.swift:138:

macAddress = IORegistryEntryCreateCFProperty(parentService, "IOMACAddress" as CFString, kCFAllocatorDefault, 0).takeRetainedValue() as? NSData

Any plans for macOS?

Hey Andrew.

Thanks for such a great library. Do you have any plans for porting it to macOS?

ReceiptValidationError error 3

After your great tutorial and a few modifications I am now getting:

ReceiptValidationError error 3

Yet, when I use the "fetchReceipt()" you also provided, it shows that YES THE RECEIPT is found.

Please help.

Not compiling with OpenSSL 1.0.210

I'm currently working on a SwiftyStorekit project and want to integrate SwiftyLocalReceiptValidator for offline checking of the receipt (so also waiting for the result of issue #1 ).

When following the instructions written at https://www.andrewcbancroft.com/2015/09/21/openssl-for-ios-swift-the-easy-way/ with OpenSSL 1.0.210, Swift 4 and Xcode 9 I seem to hit a few new roadblocks. There are a lot of undeclared references, I've managed to solve most of them by including a few extra headers in the bridging header:

#import <openssl/sha.h>
#import <openssl/x509.h>

But that still leaves one undeclared reference: the pkcs7_d_sign () function at https://github.com/andrewcbancroft/SwiftyLocalReceiptValidator/blob/master/ReceiptValidator.swift#L174

I can't seem to find any mention of this function in the OpenSSL headers, and when searching online I only get references to Swift receipt validation. My guess is that this function was removed in the last two years, could you help me with a workaround?

Cannot trace any variables after intergration with SwiftyLocalReceiptionValidator or openssl

(lldb) po infoDictionary
warning: Swift error in module Reading.
Debug info from this module will be unavailable in the debugger.

error: in auto-import:
failed to get module 'Reading' from AST context:
/Users/foolbear/Jobs/Reading/Reading/Reading-Bridging-Header.h:20:9: note: in file included from /Users/foolbear/Jobs/Reading/Reading/Reading-Bridging-Header.h:20:
#import <openssl/x509.h>
^

/Users/foolbear/Jobs/Reading/Reading/Utilities/openssl/openssl.framework/Headers/x509.h:96:13: note: in file included from /Users/foolbear/Jobs/Reading/Reading/Utilities/openssl/openssl.framework/Headers/x509.h:96:

include <openssl/rsa.h>

        ^

error: /Users/foolbear/Jobs/Reading/Reading/Utilities/openssl/openssl.framework/Headers/rsa.h:96:51: error: expected ')'
int (*rsa_mod_exp) (BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx);
^

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk/usr/include/complex.h:42:11: note: expanded from macro 'I'
#define I _Complex_I
^

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk/usr/include/complex.h:40:21: note: expanded from macro '_Complex_I'
#define _Complex_I (extension 1.0iF)
^

/Users/foolbear/Jobs/Reading/Reading/Utilities/openssl/openssl.framework/Headers/rsa.h:96:51: note: to match this '('
int (*rsa_mod_exp) (BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx);
^

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk/usr/include/complex.h:42:11: note: expanded from macro 'I'
#define I _Complex_I
^

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.2.sdk/usr/include/complex.h:40:20: note: expanded from macro '_Complex_I'
#define _Complex_I (extension 1.0iF)
^

error: failed to import bridging header '/Users/foolbear/Jobs/Reading/Reading/Reading-Bridging-Header.h'

(lldb)

Help Request - Ideas?

Hey there!

I've run through all install instructions, and compared the demo project to mine extensively, and I can't figure out what is going wrong. I'm basically getting all of the errors this guy is getting:

https://stackoverflow.com/questions/46340406/xcode-9-undefined-symbols-for-architecture-arm64-within-rmappreceipt

I even upgraded your demo project to Swift 5, and recommended settings to match as much as possible and it still works fine.

I noticed that the OpenSSL files are checked-in, and wasn't sure if this might be the reason your project is working - I.E. not using the compiled output from the pod.

Do you have any ideas or recommendations? Any insight would be hugely appreciated.

Use of unresolved type X509

Hi there. I'm attempting to compile the ReceiptValidator in my project and running in to a few errors.
Use of undeclared type X509
Use of unresolved identifier OpenSSL_add_all_digests
Use of unresolved identifier c2i_ASN1_INTEGER - This has been raised and a workaround identified in a different thread so lets ignore it for now!

I'm using openssl 1.1.1 using a script similar to that in the OpenSSL pod. I have added the bridging header as per the demo project. My understanding is a module map is NOT required (as we are using bridging headers)?

Any assistance is most appreciated. Thanks!

Merge this project into SwiftyStoreKit

Hi Andrew,

I'm the author of SwiftyStoreKit, which a widely used and very popular library in the iOS community.

One of the missing features in SwiftyStoreKit is local receipt validation, and I feel that your project would be a great addition.

I feel the iOS community as a whole would benefit if we could incorporate your implementation into SwiftyStoreKit, and would make local receipt validation immediately available to all the developers that are already using it.

What do you think?

Best,

Andrea

Integrating with SwiftyStoreKit

Hi Andrew

I'm looking into using your code as the base for local validation in my SwiftyStoreKit project. I noticed you guys were chatting a while ago about integrating the two projects, but nothing ever came of it.

Have you got any tips for 'stripping down' your code to create a new class that conforms to SwiftyStoreKit's ReceiptValidator protocol? Any do's and don'ts?

Thanks for your guidance in advance

Emile

Use of unresolved identifier c2i_ASN1_INTEGER

I'm trying to use code similar to this, however with the version of OpenSSL (v1.1.1) that I have compiled myself (Using a similar script to the CocoaPod you use) I get Use of unresolved identifier c2i_ASN1_INTEGER. It seems like that file is in the Crypto framework rather than the OpenSSL framework, but even if I get that into an importable state it doesn't work ๐Ÿ˜ž

I'm very new to cryptography and trying to use OpenSSL, do you have any ideas?

Missing documentation

The linked OpenSSL tutorial or the README seem to be missing a critical step to get this building:

Add the following to HEADER_SEARCH_PATHS
"${PODS_ROOT}/Headers/Public"
"${PODS_ROOT}/Headers/Public/OpenSSL"

Comments welcome

Weird Bug

Hi,

First first for sharing your library.

When I add your code to my app the debug var window stop providing values for any variable in the app. If I remove #import <openssl/x509.h> from bridging header it start working again.

Any idea of what can cause this? I'm surprise because I did implement your code in another app and everything works fine ...

Thanks ;-)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.