{"id":20492553,"url":"https://github.com/redmadrobot/pinkman","last_synced_at":"2025-04-13T17:03:22.839Z","repository":{"id":40640554,"uuid":"273254357","full_name":"RedMadRobot/PINkman","owner":"RedMadRobot","description":"PINkman is a library to help implementing an authentication by a PIN code in a secure manner. The library derives hash from the user's PIN using Argon2 function and stores it in an encrypted file. The file is encrypted with the AES-256 algorithm in the GCM mode and keys are stored in the AndroidKeystore.","archived":false,"fork":false,"pushed_at":"2023-01-26T09:25:56.000Z","size":461,"stargazers_count":85,"open_issues_count":2,"forks_count":10,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-04-30T13:35:16.512Z","etag":null,"topics":["android-library","android-security","argon2","authentication","kotlin","kotlin-android","kotlin-library","user-pin"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RedMadRobot.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-06-18T14:04:32.000Z","updated_at":"2024-04-14T05:12:18.000Z","dependencies_parsed_at":"2023-02-14T15:46:26.973Z","dependency_job_id":null,"html_url":"https://github.com/RedMadRobot/PINkman","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2FPINkman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2FPINkman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2FPINkman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RedMadRobot%2FPINkman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RedMadRobot","download_url":"https://codeload.github.com/RedMadRobot/PINkman/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224819480,"owners_count":17375274,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android-library","android-security","argon2","authentication","kotlin","kotlin-android","kotlin-library","user-pin"],"created_at":"2024-11-15T17:29:36.346Z","updated_at":"2024-11-15T17:29:37.424Z","avatar_url":"https://github.com/RedMadRobot.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](img/logo.svg)\n\n# PINkman\n[![API](https://img.shields.io/badge/API-23%2B-red.svg?style=flat)](https://android-arsenal.com/api?level=23)\n![CI](https://github.com/RedMadRobot/PINkman/workflows/CI/badge.svg)\n![Maven Central](https://img.shields.io/maven-central/v/com.redmadrobot/pinkman)\n\n\nImplementing an authentication by a PIN code is an ordinary task for a\nmobile applications developer. You can even think of it as some kind of\nboilerplate code. But it's a trap. Such tasks have a number of security\ngotchas. Therefore there's a high risk of implementing it in an insecure\nmanner. Don't worry, Pinkman to the rescue!\n\n## What is it?\n\nPINkman is a library to help implementing an authentication by a PIN\ncode in a secure manner. The library derives hash from the user's PIN\nusing [Argon2](https://github.com/P-H-C/phc-winner-argon2) hash function  \nand stores it in an encrypted file. The file is encrypted with the\nAES-256 algorithm in the GCM mode and keys are stored in the\nAndroidKeystore.\n\n## How it works?\n\nThis library doesn't reinvent it's own cryptograhy and just stands on\nthe shoulders of giants. Here's the description of the used technologies\nand their params.\n\n##### Deriving a hash from a PIN code\n\nFor getting the hash, the Argon2 function is used with following params:\n\n- **Mode**: Argon2i\n- **Time cost in iterations**: 5\n- **Memory cost in KBytes**: 65 536\n- **Parallelism**: 2\n- **Derived hash length**: 128bit\n\n##### Encrypted files\n\nTo store data securely, this library is using the\n[Jetpack security](https://developer.android.com/jetpack/androidx/releases/security)\nlibrary from the\n[Android Jetpack libraries suite](https://developer.android.com/jetpack).\nThat library, in turn, is using the other awesome library -\n[Tink](https://github.com/google/tink), so you can be sure that storing\ndata of a PIN code is organized quite secure. Or you can verify it\nyourself ;)\n\n\n## Quick start\n\nAdd this library to your gradle config\n\n```groovy\nimplementation 'com.redmadrobot:pinkman:$pinkman_version'\n```\n\nCreate an instance of the `Pinkman` class (use a DI please) and\nintegrate it to your authentication logic.\n\n```kotlin\nval pinkman = Pinkman(application.applicationContext)\n\n...\n\nclass CreatePinViewModel(private val pinkman: Pinkman) : ViewModel() {\n\n    val pinIsCreated = MutableLiveData\u003cBoolean\u003e()\n\n    fun createPin(pin: String) {\n        pinkman.createPin(pin)\n\n        pinIsCreated.postValue(true)\n    }\n}\n\n...\n\nclass InputPinViewModel(private val pinkman: Pinkman) : ViewModel() {\n\n    val pinIsValid = MutableLiveData\u003cBoolean\u003e()\n\n    fun validatePin(pin: String) {\n        pinIsValid.value = pinkman.isValidPin(pin)\n    }\n}\n```\n\nPinkman uses [StrongBox](https://developer.android.com/training/articles/keystore#HardwareSecurityModule) by default. \nIf you want to turn it off you can do it in `Pinkman.Config` class.\n\n```kotlin\nval pinkman = Pinkman(\n    application.applicationContext,\n    config = Pinkman.Config(useStrongBoxIfPossible = false)\n)\n```\n\nAlso you can do all these things even sipmler with the UI components\n(`PinView` and `PinKeyboard`) supplied by this library. You need to add\nthis dependency to use them\n\n```groovy\nimplementation 'com.redmadrobot:pinkman-ui:$pinkman_version' \n```\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003candroidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\u003e\n\n    \u003ccom.redmadrobot.pinkman_ui.PinView\n        android:id=\"@+id/pin_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"75dp\"\n        app:emptyDrawable=\"@drawable/circle_grey\"\n        app:filledDrawable=\"@drawable/circle_red\"\n        app:itemHeight=\"22dp\"\n        app:itemWidth=\"22dp\"\n        app:layout_constraintBottom_toTopOf=\"@+id/keyboard\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:length=\"4\"\n        app:spaceBetween=\"28dp\" /\u003e\n\n    \u003ccom.redmadrobot.pinkman_ui.PinKeyboard\n        android:id=\"@+id/keyboard\"\n        style=\"@style/PinkmanDefaultKeyboard\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\" /\u003e\n\n\u003c/androidx.constraintlayout.widget.ConstraintLayout\u003e\n\n```\n\nAnd integrate them with the logic written before\n\n```kotlin\nclass CreatePinFragment : Fragment() {\n\n    private val viewModel: CreatePinViewModel by viewModels()\n\n    ...\n    \n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        viewModel.pinIsCreated.observe(viewLifecycleOwner, Observer { isCreated -\u003e\n            findNavController().popBackStack(R.id.mainFragment, false)\n        })\n\n        pin_view.onFilledListener = { viewModel.createPin(it) }\n        keyboard.keyboardClickListener = { pin_view.add(it) }\n\n    }\n}\n```\n\n⚠️  \nHash deriving operations can take significant time on some devices. In\norder to avoid ANR in your application you shouldn't run methods\n`createPin()`, `changePin()`, `isValidPin()` on the main thread.\n\nThis library has already provided two extensions to run these methods\nasynchronously. You can choose one depending on your specific needs (or\ntech stack).\n\nYou need to add this dependency if you prefer RxJava:\n\n```groovy\nimplementation 'com.redmadrobot:pinkman-rx3:$pinkman_version'\n```\n\nBut if you're on the bleeding edge technologies, you should use a\ndependency with\n[Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)\nsupport:\n\n```groovy\nimplementation 'com.redmadrobot:pinkman-coroutines:$pinkman_version'\n```\n\nAs a result, you'll get RxJava specific or Coroutines specific method's\nset:\n\n```kotlin\n// RxJava3\nfun createPinAsync(...): Completable\nfun changePinAsync(...): Completable\nfun isValidPinAsync(...): Single\u003cBoolean\u003e\n\n// Coroutines\nsuspend fun createPinAsync(...)\nsuspend fun changePinAsync(...)\nsuspend fun isValidPinAsync(...): Boolean\n```\n\n## Feedback\n\nIn case you have faced any bugs or have any useful suggestions for\nimprovement of this library, feel free to create an\n[issue](https://github.com/RedMadRobot/PINkman/issues).\n\n## LICENSE\n\n\u003eTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n\u003eOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n\u003eMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n\u003eIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n\u003eCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n\u003eTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n\u003eSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredmadrobot%2Fpinkman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fredmadrobot%2Fpinkman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredmadrobot%2Fpinkman/lists"}