Giter Site home page Giter Site logo

ncryptf / ncryptf-kotlin Goto Github PK

View Code? Open in Web Editor NEW
1.0 2.0 0.0 187 KB

ncryptf for Android, written in Kotlin

License: BSD 3-Clause "New" or "Revised" License

Kotlin 99.78% IDL 0.22%
ncryptf hkdf hmac authentication api end-to-end-encryption sodium libsoidum security kotlin

ncryptf-kotlin's Introduction

ncryptf Kotlin (Android)

TravisCI License

ncryptf logo

A Android library written in Kotlin for facilitating hashed based KDF signature authentication, and end-to-end encrypted communication with compatible API's.

Android

This is an Android Kotlin library. If you're looking for general Kotlin or Java support, check out ncryptf-java instead. This libray should have full Java interop for Android applications written in Java instead of Kotlin.

Installing

This library can be installed through JitPack:

Gradle

Kotlin build.gradlle.kts

  1. Add jitpack.io to your root bundle.gradle file:
allprojects {
    repositories {
        ...
        maven(url = 'https://jitpack.io')
    }
}
  1. Add this repository as a dependency. Be sure to replace LATEST_TAG_FROM_GITHUB appropriately.
dependencies {
    implementation("com.ncryptf:ncryptf-kotlin:LATEST_TAG_FROM_GITHUB")
}

Java build.gradle

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
  1. Add this repository as a dependency. Be sure to replace LATEST_TAG_FROM_GITHUB appropriately.
dependencies {
    implementation "com.github.ncryptf:ncryptf-kotlin:LATEST_TAG_FROM_GITHUB" 
}

Documentation

Javadoc documentation can be generated by running ./gradle dokka, then opening the HTML documentation in ./app/build/javadoc/app/index.html.

Testing

export ANDROID_HOME = /path/to/Android/sdk
./gradlew clean build cAT

Note that you need a running android emulator, or an attached Android device for the androidTest suite to run.

HMAC+HKDF Authentication

HMAC+HKDF Authentication is an Authentication method that allows ensures the request is not tampered with in transit. This provides resiliance not only against network layer manipulation, but also man-in-the-middle attacks.

At a high level, an HMAC signature is created based upon the raw request body, the HTTP method, the URI (with query parameters, if present), and the current date. In addition to ensuring the request cannot be manipulated in transit, it also ensures that the request is timeboxed, effectively preventing replay attacks.

The library itself is made available by importing the following struct:

Supporting API's will return the following payload containing at minimum the following information.

{
    "access_token": "7XF56VIP7ZQQOLGHM6MRIK56S2QS363ULNB5UKNFMJRQVYHQH7IA",
    "refresh_token": "MA2JX5FXWS57DHW4OIHHQDCJVGS3ZKKFCL7XM4GNOB567I6ER4LQ",
    "ikm": "bDEyECRvKKE8w81fX4hz/52cvHsFPMGeJ+a9fGaVvWM=",
    "signing": "7v/CdiGoEI7bcj7R2EyDPH5nrCd2+7rHYNACB+Kf2FMx405und2KenGjNpCBPv0jOiptfHJHiY3lldAQTGCdqw==",
    "expires_at": 1472678411
}

After extracting the elements, we can create signed request by doing the following:

import com.ncryptf.android.Token
import com.ncryptf.android.Authorization
import com.ncryptf.android.exceptions.*

val token: Token = Token(
    accessToken,
    refreshToken,
    ikm,
    signing,
    expiresAt
)

try {
    val auth: Authorization = Authorization(
        httpMethod,
        uri,
        token,
        date,
        payload
    )

    val header: String = auth.getHeader()
} catch(e: KeyDerivationException) {
    // Handle errors
}

A trivial full example is shown as follows:

import com.ncryptf.android.Token;
import com.ncryptf.android.Authorization
import com.ncryptf.android.exceptions.*
import java.util.Base64
import java.time.Instant
import java.time.ZoneOffset
import java.time.ZonedDateTime

val token: Token = Token(
    "7XF56VIP7ZQQOLGHM6MRIK56S2QS363ULNB5UKNFMJRQVYHQH7IA",
    "7XF56VIP7ZQQOLGHM6MRIK56S2QS363ULNB5UKNFMJRQVYHQH7IA",
    Base64.getDecoder().decode("bDEyECRvKKE8w81fX4hz/52cvHsFPMGeJ+a9fGaVvWM="),
    Base64.getDecoder().decode("7v/CdiGoEI7bcj7R2EyDPH5nrCd2+7rHYNACB+Kf2FMx405und2KenGjNpCBPv0jOiptfHJHiY3lldAQTGCdqw==")
    ZonedDateTime.ofInstant(Instant.ofEpochSecond(1472678411), ZoneOffset.UTC)
)

val date: ZonedDateTime = ZonedDateTime.now(ZoneOffset.UTC)

try {
    val auth: Authorization = Authorization(
        "POST",
        "/api/v1/test",
        token,
        date,
        "{\"foo\":\"bar\"}"
    );

    val header: String = auth.getHeader();
} catch (e: KeyDerivationException) {
    // Handle errors
}

Note that the date property should be pore-offset when calling Authorization to prevent time skewing.

The payload parameter should be a JSON serializable string.

Version 2 HMAC Header

The Version 2 HMAC header, for API's that support it can be retrieved by calling:

val header: String = auth.getHeader();

Version 1 HMAC Header

For API's using version 1 of the HMAC header, call Authorization with the optional version parameter set to 1 for the 6th parameter.

try {
    val auth: Authorization = Authorization(
        httpMethod,
        uri,
        token,
        date,
        payload,
        1
    );

    val header: String = auth.getHeader();
} catch (e: KeyDerivationException) {
    // Handle errors
}

This string can be used in the Authorization Header

Date Header

The Version 1 HMAC header requires an additional X-Date header. The X-Date header can be retrieved by calling auth.getDateString()

Encrypted Requests & Responses

This library enables clients to establish and trusted encrypted session on top of a TLS layer, while simultaniously (and independently) providing the ability authenticate and identify a client via HMAC+HKDF style authentication.

The rationale for this functionality includes but is not limited to:

  1. Necessity for extra layer of security
  2. Lack of trust in the network or TLS itself (see https://blog.cloudflare.com/incident-report-on-memory-leak-caused-by-cloudflare-parser-bug/)
  3. Need to ensure confidentiality of the Initial Key Material (IKM) provided by the server for HMAC+HKDF authentication
  4. Need to ensure confidentiality of user submitted credentials to the API for authentication

The primary reason you may want to establish an encrypted session with the API itself is to ensure confidentiality of the IKM to prevent data leakages over untrusted networks to avoid information being exposed in a Cloudflare like incident (or any man-in-the-middle attack). Encrypted sessions enable you to utilize a service like Cloudflare should a memory leak occur again with confidence that the IKM and other secure data would not be exposed.

To encrypt, decrypt, sign, and verify messages, you'll need to be able to generate the appropriate keys. Internally, this library uses lazysodium-java to perform all necessary cryptography functions, though any libsodium implementation for Android would suffice.

Encryption Keys

Encryption uses a sodium crypto box. A keypair can be generated as follows

import com.ncryptf.android.Utils;
import com.ncryptf.android.Keypair;
val kp = Utils.generateKeypair() as? Keypair

Signing Keys

Encryption uses a sodium signature. A keypair for signing can be generated as follows using lazy-sodium:

import com.ncryptf.android.Utils
import com.ncryptf.android.Keypair
val kp = Utils.generateSigningKeypair() as? Keypair

Encrypted Request Body

Payloads can be encrypted as follows:

import com.ncryptf.android.Request
import com.ncryptf.exceptions.*
import java.util.Base64

// Arbitrary string payload
val payload: String = "{\"foo\":\"bar\"}";

try {
    // 32 byte secret and public key. Extract from kp.get...().getAsBytes(), or another libsodium method
    val request: Request = Request(secretKeyBytes, signatureSecretKey /* token.signature */);

    // Cipher now contains the encryted data
    // Signature should be the signature private key previously agreed upon with the sender
    // If you're using a `Token` object, this should be the `.signature` property
    val cipher: ByteArray = request.encrypt(payload, clientPublicKey)

    // Send as encrypted request body
    val b64Body: String = Base64.getEncoder().encode(cipher)

    // Do your http request here
} catch (e: EncryptionFailedException) {
    // Handle encryption errors here
}

Note that you need to have a pre-bootstrapped public key to encrypt data. For the v1 API, this is typically this is returned by /api/v1/server/otk.

Decrypting Responses

Responses from the server can be decrypted as follows:

import com.ncryptf.Response
import com.ncryptf.exceptions.*
import java.util.Base64

try {
    // Grab the raw response from the server
    val responseFromServer: ByteArray = Base64.getDecoder().decode("<HTTP-Response-Body>")
    val response: Response = Response(clientSecretKey)

    val decrypted = response.decrypt(responseFromServer) as? String
    
} catch (e: InvalidChecksumException) {
    // Checksum is not valid. Request body was tampered with
} catch (e: InvalidSignatureException) {
    // Signature verification failed
} catch (e: DecryptionFailedException) {
    // Decryption failed. This may be an issue with the provided nonce, or keypair being used
}

V2 Encrypted Payload

Verison 2 works identical to the version 1 payload, with the exception that all components needed to decrypt the message are bundled within the payload itself, rather than broken out into separate headers. This alleviates developer concerns with needing to manage multiple headers.

The version 2 payload is described as follows. Each component is concatanated together.

Segment Length
4 byte header DE259002 in binary format 4 BYTES
Nonce 24 BYTES
The public key associated to the private key 32 BYTES
Encrypted Body X BYTES + 16 BYTE MAC
Signature Public Key 32 BYTES
Signature or raw request body 64 BYTES
Checksum of prior elements concatonated together 64 BYTES

ncryptf-kotlin's People

Contributors

charlesportwoodii avatar

Stargazers

 avatar

Watchers

 avatar  avatar

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.