{"id":21513246,"url":"https://github.com/patrickfav/armadillo","last_synced_at":"2025-04-05T20:03:06.886Z","repository":{"id":41612320,"uuid":"114701607","full_name":"patrickfav/armadillo","owner":"patrickfav","description":"A shared preference implementation for confidential data in Android. Per default uses AES-GCM, BCrypt and HKDF as cryptographic primitives. Uses the concept of device fingerprinting combined with optional user provided passwords and strong password hashes.","archived":false,"fork":false,"pushed_at":"2023-04-01T06:17:12.000Z","size":2429,"stargazers_count":304,"open_issues_count":3,"forks_count":51,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-03-29T19:07:13.263Z","etag":null,"topics":["aes-encryption","aes-gcm","android","authenticated-encryption","bcrypt","crypto","cryptography","hkdf","security","sharedpreferences"],"latest_commit_sha":null,"homepage":"https://favr.dev/opensource/armadillo","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/patrickfav.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-12-19T00:35:28.000Z","updated_at":"2025-03-25T16:32:34.000Z","dependencies_parsed_at":"2022-07-07T01:01:44.907Z","dependency_job_id":"49606f74-2c5a-47cf-9a80-22c2ebd77de2","html_url":"https://github.com/patrickfav/armadillo","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickfav%2Farmadillo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickfav%2Farmadillo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickfav%2Farmadillo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickfav%2Farmadillo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/patrickfav","download_url":"https://codeload.github.com/patrickfav/armadillo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247393547,"owners_count":20931811,"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":["aes-encryption","aes-gcm","android","authenticated-encryption","bcrypt","crypto","cryptography","hkdf","security","sharedpreferences"],"created_at":"2024-11-23T22:55:08.480Z","updated_at":"2025-04-05T20:03:06.847Z","avatar_url":"https://github.com/patrickfav.png","language":"Java","readme":"# Armadillo - Encrypted Shared Preference\n\n\n\u003cimg src=\"https://github.com/patrickfav/armadillo/blob/main/doc/logo/logo_ldpi.png?raw=true\" align=\"right\"\n     alt=\"Armadillo Logo\" width=\"170\" height=\"207\"\u003e\n\nA shared preference implementation for secret data providing confidentiality,\nintegrity and authenticity. Per default uses AES-GCM, BCrypt and HKDF as cryptographic primitives.\n\n[![Maven Central](https://img.shields.io/maven-central/v/at.favre.lib/armadillo)](https://mvnrepository.com/artifact/at.favre.lib/armadillo)\n[![Build Status](https://travis-ci.com/patrickfav/armadillo.svg?branch=master)](https://travis-ci.com/patrickfav/armadillo)\n[![Javadocs](https://www.javadoc.io/badge/at.favre.lib/armadillo.svg)](https://www.javadoc.io/doc/at.favre.lib/armadillo)\n[![Coverage Status](https://coveralls.io/repos/github/patrickfav/armadillo/badge.svg?branch=master)](https://coveralls.io/github/patrickfav/armadillo?branch=master)\n[![Maintainability](https://api.codeclimate.com/v1/badges/c3ac3cdd849ba47fd90a/maintainability)](https://codeclimate.com/github/patrickfav/armadillo/maintainability)\n\n**Important Notice:** If you migrate to v0.6.0 and use a user password and\ndefault key stretching function migration is needed due to a security issue.\nSee [migration guide in the changelog for v0.6.0](https://github.com/patrickfav/armadillo/releases)\n\n## Features\n\n* **No-Nonse State-of-the-Art Crypto**: Authenticated Encryption with [AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard)-[GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode), key derivation functions [Bcrypt](https://en.wikipedia.org/wiki/Bcrypt) and [HKDF](https://en.wikipedia.org/wiki/HKDF)\n* **Flexible**: Tons of nobs and switches while having sane defaults\n* **Modular**: use your own implementation of symmetric cipher, key stretching, data obfuscation, etc.\n* **Lightweight**: No massive dependencies required like [BouncyCastle](https://www.bouncycastle.org/) or [Facebook Conceal](https://github.com/facebook/conceal)\n\n### Security Summary\n\n* Using it **with a user provided password** (and strong password hash, like the default BCrypt): **your data is strongly encrypted**\n* Using it without a user provided password: **your data is obfuscated and cannot be easily altered or read by an attacker with access to the device**\n* By using fingerprinting, it is **not easily possible to just copy data over to another device** and use it there\n* Encryption is **non-deterministic**, which means even if you encrypt the same data it **appears to be different**\n* All encrypted data is **protected against modification by an outside attacker**, so long as the encryption itself is not circumvented\n* The **[Android Keystore System](https://developer.android.com/training/articles/keystore.html) is not used**, since it proved to be unreliable and hard to handle in production due to device fragmentation and poor driver support (read more [below](https://github.com/patrickfav/armadillo#design-choices)). This implementation is a good fallback solution for a system that uses the aforementioned.\n\n## Quick Start\n\nAdd the following to your dependencies ([add jcenter to your repositories](https://developer.android.com/studio/build/index.html#top-level) if you haven't)\n\n```groovy\ncompile 'at.favre.lib:armadillo:x.y.z'\n```\n\nA very minimal example\n\n```java\nSharedPreferences preferences = Armadillo.create(context, \"myPrefs\")\n    .encryptionFingerprint(context)\n    .build();\n\npreferences.edit().putString(\"key1\", \"stringValue\").commit();\nString s = preferences.getString(\"key1\", null);\n```\n\n### Advanced Example\n\nThe following example shows some of the configurations available to the developer:\n\n```java\nString userId = ...\nSharedPreferences preferences = Armadillo.create(context, \"myCustomPreferences\")\n        .password(\"mySuperSecretPassword\".toCharArray()) //use user provided password\n        .securityProvider(Security.getProvider(\"BC\")) //use bouncy-castle security provider\n        .keyStretchingFunction(new PBKDF2KeyStretcher()) //use PBKDF2 as user password kdf\n        .contentKeyDigest(Bytes.from(getAndroidId(context)).array()) //use custom content key digest salt\n        .secureRandom(new SecureRandom()) //provide your own secure random for salt/iv generation\n        .encryptionFingerprint(context, userId.getBytes(StandardCharsets.UTF_8)) //add the user id to fingerprint\n        .supportVerifyPassword(true) //enables optional password validation support `.isValidPassword()`\n        .enableKitKatSupport(true) //enable optional kitkat support\n        .enableDerivedPasswordCache(true) //enable caching for derived password making consecutive getters faster\n        .build();\n```\n\nA xml file named like `f1a4e61ffb59c6e6a3d6ceae9a20cb5726aade06.xml` will\nbe created with the resulting data looking something like that after the\nfirst put operation:\n\n```xml\n\u003c?xml version='1.0' encoding='utf-8' standalone='yes' ?\u003e\n\u003cmap\u003e\n    \u003c!-- storage random salt --\u003e\n    \u003cstring name=\"585d6f0f415682ace841fb50d5980d60ed23a2ef\"\u003eriIPjrL2WRfoh8QJXu7fWk4GGeAKlQoJl9ofABHZKlc=\u003c/string\u003e\n    \u003c!-- 'key1':'stringValue' --\u003e\n    \u003cstring name=\"152b866fd2d63899678c21f247bb6df0d2e38072\"\u003eAAAAABD/8an1zfovjJB/2MFOT9ncAAAAJaf+Z9xgzwXzp1BqTsVMnRZxR/HfRcO8lEhyKpL17QmZ5amwAYQ=\u003c/string\u003e\n\u003c/map\u003e\n\n```\n\n### KitKat Support\n\nUnfortunately [Android SDK 19 (KITKAT)](https://en.wikipedia.org/wiki/Android_KitKat) does not fully support AES GCM mode.\nTherefore a backwards compatible implementation of AES using [CBC](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_Block_Chaining_(CBC))\nwith [Encrypt-then-MAC](https://en.wikipedia.org/wiki/Authenticated_encryption#Encrypt-then-MAC_(EtM))\ncan be used to support this library on older devices. This should provide\nthe same security strength as the GCM version, however the support must\nbe enabled manually:\n\n\n```java\nSharedPreferences preferences = Armadillo.create(context, \"myCustomPreferences\")\n        .enableKitKatSupport(true)\n        ...\n        .build();\n```\n\nIn this mode, if on a KitKat device the backwards-compatible implementation is\nused, the default AES-GCM version otherwise. Upgrading to a newer OS version\nthe content should still be decryptable, while newer content will then be\nencrypted with the AES-GCM version.\n\n## Description\n\n### Design Choices\n\n* **AES + GCM block mode:** To make sure that the data is not only kept\nconfidential, but it's integrity also preserved, the authenticated encryption\n[AES+GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode) is used. GCM\ncan be implemented efficiently and fast and is the usually alternative to\n[encrypt then mac](https://en.wikipedia.org/wiki/Authenticated_encryption#Encrypt-then-MAC_(EtM))\nwith AES+CBC and HMAC. The authentication tag is appended to the message and\nis 16 byte long in this implementation. A downside of GCM is the requirement\nto [never reuse](https://en.wikipedia.org/wiki/Galois/Counter_Mode#Security)\n a [IV](https://en.wikipedia.org/wiki/Initialization_vector) with the same key,\n which is avoided in this lib.\n* **Every put operation creates a different cipher text:** Every put operation\ngenerates new salts, iv so the the resulting cipher text will be unrecognizably\ndifferent even with the same underlying data. This makes it harder to check if\nthe data actually has changed.\n* **KDFs with Key Stretching features for user passwords:** Add brute-force\nprotection to possibly weak user provided passwords (e.g. BCrypt).\n* **Minimum SDK 19 (Android 4.4):** A way to increase security is to cap older\nimplementation. SDK 19 seems to be a good compromise where most of the older\n[security hack fixes](https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html)\n are not necessary anymore, but still targeting most devices.\n* **Use of [JCA as Provider](https://issuetracker.google.com/issues/36983155) for cryptographic primitives:**\nVarious security frameworks exists in Java: [BouncyCastle](https://www.bouncycastle.org/),\n[Conscrypt](https://github.com/google/conscrypt), [Facebook Conceal](https://github.com/facebook/conceal).\nThe problem is that these libraries are usually huge and require manual\nupdates to have all the latest security fixes.\nThis library however depends on the default JCA provider (although the developer may choose a\ndifferent one). This puts trust in the device and it's implementation, while\nexpecting frequent security patches. Usually the default provider since KitKat is\n[`AndroidOpenSSL`]() provider which is fast (probably hardware accelerated for e.g. AES) and\nheavily used by e.g. TLS implementation.\n* **[Android Keystore System](https://developer.android.com/training/articles/keystore.html) is not used:**\n  In my humble opinion, the Android Keystore is the best possible way to secure\n  data on an Android device. Unfortunately, due to the massive fragmentation\n  in the ecosystem properly handling and using the Android Keystore System\n  is not easy and has [some major drawbacks](https://issuetracker.google.com/issues/36983155).\n  Due to working in a security relevant field I have a lot of experience with\n  this technology, therefore the decision was made to not support it _for this\n  implementation_ i.e. to keep it simple.\n* **Use of data obfuscation**: To disguise the actual data format and appear\nas a pseudo random byte array, obfuscation is used. This deliberately uses\nnon standard ways to make it a bit harder to reverse engineer.\n\n### User provided Passwords\n\nA high entropy value not known to any system but the user is a good and strong\nbase for a cryptographic key. Unfortunately user-based passwords are often\nweak (low-entropy). To mitigate that fact and help preventing easy brute-forcing\n[key derivation functions](https://en.wikipedia.org/wiki/Key_derivation_function)\nwith [key stretching](https://en.wikipedia.org/wiki/Key_stretching)\nproperties are used. These functions calculate pseudo-random data from it's\nsource material which requires mandatory work.\n\nThe following implementations are available:\n\n* [BCrypt](https://en.wikipedia.org/wiki/Bcrypt): based on blowfish, has a variable cpu cost parameter and a fixed memory cost parameter (_default_)\n* [PBKDF2](https://en.wikipedia.org/wiki/PBKDF2): applies a pseudorandom function, such as hash-based message authentication code (HMAC), to the input password or passphrase along with a salt value and repeats the process many times to produce a derived key; no memory hardness\n\nIt is possible to provide any KDF implementation to the storage with providing\na custom `KeyStretchingFunction` implementation.\n\nNote, if you use key stretching put/get operations will get very slow (depeding\non the work factor of course), so consider accessing the store in a background\nthread.\n\n### Encryption Fingerprint\n\nThis store bases part of it's security on so called fingerprinting. That\nbasically means, during runtime entropy from e.g. the device, system or other\nparts are used to create a cryptographic key with which the data is encrypted.\nIt basically is encryption with a semi-secret key.\n\nThis has the following benefits:\n\n* Binding the data to the executing runtime (ie. making it harder to lift the data and trying to read it in a different environment)\n* Strongly obfuscating the data bordering actual encryption when the used fingerprint is infeasible to guess\n* Be able to scope the data to a specific environment (e.g. when using the Android OS image build number, every update invalidates the data)\n\nThis store has a default implementation of `EncryptionFingerprint` which\ncan only use generic data. In detail the following properties are incorporated:\n\n* Fingerprint of the [APK signature](https://source.android.com/security/apksigning/)\n* [Android ID](https://developer.android.com/reference/android/provider/Settings.Secure.html#ANDROID_ID) : a 64-bit number (expressed as a hexadecimal string) byte random value; on SDK 26 and higher, unique to each combination of app-signing key, user, and device - on SDK 25 lower only unique to user and device\n* [Application package name](https://developer.android.com/studio/build/application-id.html), [brand](https://developer.android.com/reference/android/os/Build.html#BRAND), [model](https://developer.android.com/reference/android/os/Build.html#MODEL) and name of the device\n* 32 byte hardcoded static random value\n\n#### Enhancing the Strength of the Encryption Fingerprint\n\nThe security of this mechanism increases considerably if the user adds it's\nown data. Here are some suggestions:\n\n* Random values hardcoded, locally generated or provided by a remote service\n* Unique user-id (if the application has the concept of login)\n* Device Serial (requires dangerous permission SDK \u003e 25)\n* Sim-ID/ICCID (if changing the sim should/can invalidate the data)\n* Android OS image build fingerprint (if you want to invalidate the data after OS update)\n\n### Key Derivation Process\n\nThe cryptographic key used to encrypt the data is composed of the following\nparts:\n\n![screenshot key derivation](doc/key_derivation.png)\n\n* User password (optional): provided by the caller and stretched with e.g. Bcrypt\n* Encryption Fingerprint (see section above)\n* Entry Key: the hashed version of the key passed by the caller; this will bind the data to that specific entry key\n* Entry Salt: a random 16 byte value unique to that specific entry that will be created on every put operation (will also be used for the key stretching function)\n* Storage Salt: a random 32 byte value unique to that specific storage, created on first creation of the storage\n\nThe concatenated key material will be derived and stretched to the desired length\nwith [HKDF](https://en.wikipedia.org/wiki/HKDF) derivation function.\n\n### Persistence Profile\n\n#### Key\n\nThe key is hashed with [HKDF](https://en.wikipedia.org/wiki/HKDF) (which uses\nHmac with Sha512 internally) expanded to a 20 byte hash which will be encoded with\n[base16 (hex)](https://en.wikipedia.org/wiki/Hexadecimal). The key generation\nis salted by the encryption fingerprint, so different shared preferences will\ngenerate different hashes for the same keys.\n\n#### Content\n\nThe diagram below illustrates the used data format. To disguise the format\na little bit it will be obfuscated by a simple xor cipher.\n\n![screenshot gallery](doc/persistence_profile.png)\n\nThe resulting data will be encoded with [base64](https://en.wikipedia.org/wiki/Base64) and looks like this in the shared preferences xml:\n\n```xml\n\u003c?xml version='1.0' encoding='utf-8' standalone='yes' ?\u003e\n\u003cmap\u003e\n    \u003cstring name=\"39e3e4f83dda81c44f8a9063196b28b3d5091fca\"\u003ehwbchXlqDAQcig6q3UWxdbOb2wouDGGwjUGNIzREiy0=\u003c/string\u003e\n    \u003cstring name=\"62ef41ac992322bdd669e96799c12a66a2cce111\"\u003eAAAAABAAajtOaVCq5yqu1TPxgLu2AAAAqUTxgPcAM6lyNTGgy7ZAoCjqcCdtxT6T\u003c/string\u003e\n\u003c/map\u003e\n```\n\n## Digital Signatures\n\n### Signed Commits\n\nAll tags and commits by me are signed with git with my private key:\n\n    GPG key ID: 4FDF85343912A3AB\n    Fingerprint: 2FB392FB05158589B767960C4FDF85343912A3AB\n\n## Build\n\nAssemble the lib with the following command\n\n    ./gradlew :armadillo:assemble\n\nThe `.aar` files can then be found in `/armadillo/build/outputs/aar` folder\n\n## Libraries \u0026 Credits\n\n* [BCrypt](https://github.com/patrickfav/bcrypt)\n* [HKDF Standalone](https://github.com/patrickfav/hkdf)\n* [Bytes](https://github.com/patrickfav/bytes-java)\n* [Awesome logo made by @iqbalhood](https://github.com/iqbalhood)\n\n## Similar Projects:\n\n* [Google's Android Jetpack EncryptedSharedPreferences using Android Keystore System](https://developer.android.com/topic/security/data)\n* [secure-preferences using AES-CBC](https://github.com/scottyab/secure-preferences) by [@scottyab](https://github.com/scottyab) (_not maintained_)\n* [secure-preferences supporting Android Keystore System](https://github.com/ophio/secure-preferences) by [@ophio](https://github.com/ophio)\n* [secure-preferences using FB Conceal framework](hhttps://github.com/phamquyhai/secure-preferences) by [@phamquyhai](https://github.com/phamquyhai)\n* [secure-preferences supporting Android Keystore System](https://github.com/adorsys/secure-storage-android) by [@adorsys](https://github.com/adorsys)\n* [secure-preferences supporting Android Keystore System](https://github.com/iamMehedi/Secured-Preference-Store) by [@iamMehedi](https://github.com/iamMehedi)\n* [secure-preferences using AES-CBC](https://github.com/PDDStudio/EncryptedPreferences) by [@PDDStudio](https://github.com/PDDStudio)\n\n## Further Reading\n\n* [Security Best Practices: Symmetric Encryption with AES-GCM in Java and Android](https://proandroiddev.com/security-best-practices-symmetric-encryption-with-aes-in-java-7616beaaade9)\n* [Security Best Practices: Symmetric Encryption with AES-CBC in Java and Android](https://proandroiddev.com/security-best-practices-symmetric-encryption-with-aes-in-java-and-android-part-2-b3b80e99ad36)\n* [Stack Overflow: Security with SharedPreferences - A Discussion](https://stackoverflow.com/a/26077852/774398)\n\n# License\n\nCopyright 2017 Patrick Favre-Bulle\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatrickfav%2Farmadillo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpatrickfav%2Farmadillo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatrickfav%2Farmadillo/lists"}