...
 
Commits (2)
# Nativescript libsodium
Use native libsodium from nativescript
Use native libsodium with nativescript. (mostly) API compatible with libsodium.js wrappers.
## (Optional) Prerequisites / Requirements
Describe the prerequisites that the user need to have installed before using your plugin. See [nativescript-firebase plugin](https://github.com/eddyverbruggen/nativescript-plugin-firebase) for example.
Only Android is supported at this time.
## Installation
Describe your plugin installation steps. Ideally it would be something like:
`tns plugin add libsodium-nativescript`
```javascript
tns plugin add <your-plugin-name>
```
For Android - libsodium-jni requires the following changes to your AndroidManifest.xml
- Add `tools:replace="android:allowBackup"` as an attribute to the application tag.
- Add `xmlns:tools="http://schemas.android.com/tools"` asn an attribute to the manifest tag.
## Usage
Describe any usage specifics for your plugin. Give examples for Android, iOS, Angular if needed. See [nativescript-drop-down](https://www.npmjs.com/package/nativescript-drop-down) for example.
```javascript
Usage code snippets here
```)
Use it the same way you use [libsodium.js](https://github.com/jedisct1/libsodium.js) including their wrapper functions.
## API
```
import { Libsodium } from "nativescript-libsodium";
let keypair = Libsodium.crypto_box_keypair();
console.log(keypair.privateKey); // It's a Uint8Array object
```
Describe your plugin methods and properties here. See [nativescript-feedback](https://github.com/EddyVerbruggen/nativescript-feedback) for example.
| Property | Default | Description |
| --- | --- | --- |
| some property | property default value | property description, default values, etc.. |
| another property | property default value | property description, default values, etc.. |
For more examples, check out the [unit tests](/demo/app/tests/tests.ts)
## Contributing
Not all libsodium functions are wrapped in this project. But they are easy to add.
Let's see how to add the crypto_box_seal_open function.
### Getting familiar
1. First run the project as is. You may run both the demo and unit tests. Read instructions [here](https://github.com/NativeScript/nativescript-plugin-seed)
2. Review docs on the function from [libsodium](https://download.libsodium.org/doc/public-key_cryptography/sealed_boxes.html) and libsodium.js
3. Review [type definitions for libsodium.js](https://gitlab.com/burke-software/passit/simple-asymmetric-js/blob/master/simple_asym/libsodium.d.ts) and [libsodium-jni](/src/libsodium-jni.d.ts)
### Adding new functions
- Add your new function (this one already exists actually) to [libsodium.common.ts](/src/libsodium.common.ts)
- Write function parameters as to match libsodium.js's wrapper.
- Convert Uint8Array to Native Byte arrays with force_java_bytes
- Run native function. Review libsodium docs (not .js)
- Convert back to Uint8Array with javvaByteArrayToUint8Array
- Add unit test demonstrating functionality.
- Submit your merge request :)
```
public static crypto_box_seal(message: string | Uint8Array, publicKey: Uint8Array): Uint8Array {
let messageBytes = this.force_java_bytes(message);
let publicKeyBytes = this.force_java_bytes(publicKey);
let ciphertext: JavaBytes = Array.create('byte', Sodium.crypto_box_sealbytes() + messageBytes.length);
Sodium.crypto_box_seal(ciphertext, messageBytes, messageBytes.length, publicKeyBytes);
return this.javaByteArrayToUint8Array(ciphertext);
}
```
## Design decisions
- We wish to maintain as close as possible API compatibility with libsodium.js wrappers. This means converting between JS Uint8Array and Java Byte Arrays. There is some performance penally for this but it allows us to "drop in" nativescript-libsodium in some projects that use libsodium.js.
- libsodium-jni is not compiled from source but from a build server. You may wish to build it yourself.
- We do not support libsodium's outputFormat param which complicates type definitions. This could change especially if someone submitted a merge request.
- Error handling could be improved - it should take the native function's return value and throw an exception if it's unable to perform the crypto.
## iOS Support?
Please contribute! You would need to refactor the android specific code to .android files and build a wrapper for the .ios ones.
It might make sense to make a generic class interface and have both Android and iOS implement it to ensure consistency.
## Need paid support?
Do you need an iOS version sooner? Or maybe prefer us to add more libsodium functions that are important to you?
Let us know at info AT burke software dot com.
## License
......
......@@ -30,4 +30,13 @@ describe("libsodium nativescript wrapper", function() {
let decryptedString = new TextDecoder('utf-8').decode(decrypted);
expect(decryptedString).toEqual(secretMessage);
});
it("can run crypto_box_seal and crypto_box_seal_open", function() {
let secretMessage = 'hello';
let keypair = Libsodium.crypto_box_keypair();
let ciphertext = Libsodium.crypto_box_seal(secretMessage, keypair.publicKey);
let decrypted = Libsodium.crypto_box_seal_open(ciphertext, keypair.publicKey, keypair.privateKey);
let decryptedString = new TextDecoder('utf-8').decode(decrypted);
expect(decryptedString).toEqual(secretMessage);
});
});
......@@ -18,6 +18,8 @@ export declare class Common {
private static trimJavaBytes(input);
static crypto_secretbox_easy(message: string | Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
static crypto_secretbox_open_easy(ciphertext: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
static crypto_box_seal(message: string | Uint8Array, publicKey: Uint8Array): Uint8Array;
static crypto_box_seal_open(ciphertext: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
static readonly crypto_aead_chacha20poly1305_ABYTES: number;
static readonly crypto_aead_chacha20poly1305_KEYBYTES: number;
static readonly crypto_aead_chacha20poly1305_NPUBBYTES: number;
......
......@@ -33,7 +33,7 @@ export class Common {
return out;
}
public static Uint8ArrayToJavaByteArray(inBytes: Uint8Array): JavaBytes {
private static Uint8ArrayToJavaByteArray(inBytes: Uint8Array): JavaBytes {
let simpleArr = Array.from(inBytes);
return simpleArr;
}
......@@ -103,6 +103,10 @@ export class Common {
return this.Uint8ArrayToJavaByteArray(jsBytes);
}
/** Remove excess 0 bytes from Java array. libsodium requires you to know the original message length
* when decrypting. However libsodium.js doesn't force you to know this. So we just make it extra big and
* trim the excess 0's. If you know a better way please let us know.
*/
private static trimJavaBytes(input: JavaBytes): JavaBytes {
let i = input.length - 1;
while (i >= 0 && input[i] === 0) {
......@@ -145,7 +149,28 @@ export class Common {
);
decrypted = this.trimJavaBytes(decrypted);
return this.javaByteArrayToUint8Array(decrypted);
}
public static crypto_box_seal(message: string | Uint8Array, publicKey: Uint8Array): Uint8Array {
let messageBytes = this.force_java_bytes(message);
let publicKeyBytes = this.force_java_bytes(publicKey);
let ciphertext: JavaBytes = Array.create('byte', Sodium.crypto_box_sealbytes() + messageBytes.length);
Sodium.crypto_box_seal(ciphertext, messageBytes, messageBytes.length, publicKeyBytes);
return this.javaByteArrayToUint8Array(ciphertext);
}
public static crypto_box_seal_open(ciphertext: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array {
let ciphertextBytes = this.force_java_bytes(ciphertext);
let publicKeyBytes = this.force_java_bytes(publicKey);
let secretKeyBytes = this.force_java_bytes(secretKey);
let decrypted: JavaBytes = Array.create('byte', Sodium.crypto_box_sealbytes() + ciphertextBytes.length);
Sodium.crypto_box_seal_open(decrypted, ciphertextBytes, ciphertextBytes.length, publicKeyBytes, secretKeyBytes);
decrypted = this.trimJavaBytes(decrypted);
return this.javaByteArrayToUint8Array(decrypted);
}
......@@ -231,11 +256,15 @@ export class Common {
);
msg += `The hash is ${hash}\n`;
// let secretMessage = 'hello';
// let nonce = this.randombytes_buf(this.crypto_secretbox_NONCEBYTES);
// let key = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5]);
// let ciphertext = this.crypto_secretbox_easy(secretMessage, nonce, key);
// let decrypted = this.crypto_secretbox_open_easy(ciphertext, nonce, key);
// let decryptedString = new TextDecoder('utf-8').decode(decrypted);
let secretMessage = 'hello';
let nonce = this.randombytes_buf(this.crypto_secretbox_NONCEBYTES);
let key = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5]);
let ciphertext = this.crypto_secretbox_easy(secretMessage, nonce, key);
let decrypted = this.crypto_secretbox_open_easy(ciphertext, nonce, key);
let ciphertext = this.crypto_box_seal(secretMessage, keypair.publicKey);
let decrypted = this.crypto_box_seal_open(ciphertext, keypair.publicKey, keypair.privateKey);
let decryptedString = new TextDecoder('utf-8').decode(decrypted);
msg += `decrypted is ${decryptedString}\n`;
......
......@@ -30,8 +30,8 @@
"Android"
],
"author": {
"name": "Your Name",
"email": "youremail@yourdomain.com"
"name": "David Burke",
"email": "david@burkesoftware.com"
},
"bugs": {
"url": "https://gitlab.com/burke-software/nativescript-libsodium/issues"
......