React Native bridge for Virgil Security Crypto Library for iOS and Android.
- Be a replacement for virgil-crypto-javascript in React Native projects
- Asymmetric Key Generation
- Encryption/Decryption of data
- Generation/Verification of digital signatures
- iOS autolinking support with React Native > 0.61
- Group encryption primitives (meant as low-level building blocks for implementing group chats in E3kit).
- Unified error names with those thrown in JS
This library's API is compatible with the virgil-crypto for JavaScript and can be used in place of the latter in React Native projects. The main difference is that in JS library a class named VirgilCrypto
is exported from the module that you need to create instances of, whereas this library exports an "instance" of that class ready to be used. Also, stream encryption is not available as there is no stream implementation in React Native. We're investigating the options to support file encryption though.
Generate a Private Key with the default algorithm (ED25519):
import { virgilCrypto } from 'react-native-virgil-crypto';
const keyPair = virgilCrypto.generateKeys();
Generate signature and sign data with a private key:
import { virgilCrypto } from 'react-native-virgil-crypto';
const signingKeypair = virgilCrypto.generateKeys();
// prepare a message
const messageToSign = 'Hello, Bob!';
// generate a signature
const signature = virgilCrypto.calculateSignature(messageToSign, signingKeypair.privateKey);
// signature is a NodeJS Buffer polyfill
console.log(signature.toString('base64'));
Verify a signature with a public key:
// verify a signature
const isVerified = virgilCrypto.verifySignature(messageToSign, signature, signingKeypair.publicKey);
Encrypt Data on a Public Key:
import { virgilCrypto } from 'react-native-virgil-crypto';
const encryptionKeypair = virgilCrypto.generateKeys();
// prepare a message
const messageToEncrypt = 'Hello, Bob!';
// encrypt the message
const encryptedData = virgilCrypto.encrypt(messageToEncrypt, encryptionKeypair.publicKey);
// encryptedData is a NodeJS Buffer polyfill
console.log(encryptedData.toString('base64'));
Decrypt the encrypted data with a Private Key:
// decrypt the encrypted data using a private key
const decryptedData = virgilCrypto.decrypt(encryptedData, encryptionKeypair.privateKey);
// convert Buffer to string
const decryptedMessage = decryptedData.toString('utf8');
To encrypt a file you will need to know its location in the file system. For images you can use React Native API, or a library such as react-native-image-picker or react-native-camera-roll-picker.
import { virgilCrypto } from 'react-native-virgil-crypto';
const keypair = virgilCrypto.generateKeys();
// this must be defined in your code
pickAnImage()
.then(image => {
return virgilCrypto.encryptFile({
// assuming `image` has a `uri` property that points to its location in file system
inputPath: image.uri,
// This can be a custom path that your application can write to
// e.g. RNFetchBlob.fs.dirs.DocumentDir + '/encrypted_uploads/' + image.fileName,
// If not specified, a temporary file will be created.
outputPath: undefined,
publicKeys: keypair.publicKey
})
.then(encryptedFilePath => {
// encryptedFilePath is the location of the encrypted file in the file system
// the original image file remain intact
// you can now upload this file using `fetch` and `FormData`, e.g.:
const data = new FormData();
data.append('photo', {
uri: 'file://' + encryptedFilePath,
type: image.type,
name: image.fileName
});
return fetch(url, { method: 'POST', body: data });
});
});
Decryption works similarly to encryption - you provide a path to the encrypted file in the file system (network urls are not supported). You can use a library such as rn-fetch-blob or react-native-fs to download a file directly into file system.
import { virgilCrypto } from 'react-native-virgil-crypto';
const keypair = virgilCrypto.generateKeys();
// this must be defined in your code
downloadImage()
.then(downloadedFilePath => {
return virgilCrypto.decryptFile({
inputPath: downloadedFilePath,
// This can be a custom path that your application can write to
// e.g. RNFetchBlob.fs.dirs.DocumentDir + '/decrypted_downloads/' + image.id + '.jpg';
// If not specified, a temporary file will be created
outputPath: undefined,
privateKey: keypair.privateKey
})
.then(decryptedFilePath => {
return <Image src={`file://${decryptedFilePath}`} />
});
});
It is also possible to calculate the digital signature of a file
import { virgilCrypto } from 'react-native-virgil-crypto';
const keypair = virgilCrypto.generateKeys();
// this must be defined in your code
pickAnImage()
.then(image => {
return virgilCrypto.generateFileSignature({
inputPath: image.uri,
privateKey: keypair.privateKey
})
.then(signature => ({ ...image, signature: signature.toString('base64') }));
});
And verify the signature of a file
import { virgilCrypto } from 'react-native-virgil-crypto';
const keypair = virgilCrypto.generateKeys();
// this must be defined in your code
downloadImage()
.then(downloadedFilePath => {
return virgilCrypto.verifyFileSignature({
inputPath: image.downloadedFilePath,
signature: image.signature,
publicKey: keypair.publicKey
})
.then(isSigantureVerified => ({ ...image, isSigantureVerified }));
});
See the sample project for a complete example of working with encrypted files.
This library only provides crypto primitives to implement group chats.
First, you need to create a group session:
import { virgilCrypto } from 'react-native-virgil-crypto';
const keypair = virgilCrypto.generateKeys();
const groupSession = virgilCrypto.generateGroupSession('unique_identifier_of_the_group');
Then, you need to share this group session with users who you want to communicate with. Note that secure sharing of group sessions is not a responsibility of this library and is left for the developer to implement. It is provided out-of-the box in E3kit, though.
// exportedSession will be an array of Buffers that this exact session can be reconstructed from
const exportedSession = groupSession.export();
// now we serialize it to send to other group members
const serializedSession = exportedSession.map(m => m.toString('base64'));
// this must be implemented by the developer
shareGroupSession(groupSession.getSessionId(), serializedSession, groupMembers);
Upon receiving a shared group session, you will need to import its serialized representation:
import { virgilCrypto } from 'react-native-virgil-crypto';
function onSharedGroupSessionReceived(sharedGroupSession) {
const sharedGroupSession = virgilCrypto.importGroupSession(sharedGroupSession);
// you can now use sharedGroupSession to encrypt and decrypt messages
}
Encryption and decryption is as easy as calling encrypt
and decrypt
methods on the group session. When encrypting, you will need to provide the current user's private key to authenticate the encrypted message:
const message = 'Hello there! This is the beginning of our group chat.';
const encryped = groupSession.encrypt(message, myPrivateKey);
When decrypting, the public key of the sender must be provided to verify the authenticity of the message:
const decrypted = groupSession.decrypt(encrypted, senderPublicKey);
console.log(decrypted.toString('utf8'));
If you need to add new members to the group, you just share the current group session with them securely. If you need to remove someone from the group, you need to add a new epoch to the group - this produces what's called an epoch message - then share that epoch message with every group member except the ones you want to remove:
groupSession.addNewEpoch();
// now we serialize it to send to other group members
const serializedSession = groupSession.export().map(m => m.toString('base64'));
// this must be implemented by the developer
shareGroupSession(groupSession.getSessionId(), serializedSession, groupMembers);
Since this is pretty low-level we do not recommend using it directly. Instead, make sure to check out the higher-level API for group chats provided by E3kit.
All of the methods of virgilCrypto
object that accept binary data, accept them in the form of string
or Buffer
. All of the methods that return binary data, return them in the form of Buffer
. We use this library as the native implementation is not available in react native. We re-export the Buffer
from the module for your convenience:
import { Buffer } from 'react-native-virgil-crypto';
console.log(Buffer.from('hello Buffer').toString('base64')); // prints aGVsbG8gQnVmZmVy
See the sample project for a complete example that you can use to measure performance of this library on your own devices.
If you want this package to work with virgil-sdk
, you'll need:
- Update the
virgil-sdk
to version 6.0.0, which is currently a pre-release. You can install it withnpm install virgil-sdk@next
. - Install one more package -
npm install @virgilsecurity/sdk-crypto
- Update the
@virgilsecurity/key-storage-rn
to the latest version, which is also currently in pre-release -npm install @virgilsecurity/key-storage-rn@next
- Install the
react-native-keychain
package, a dependency of@virgilsecurity/key-storage-rn
Here's an example of initialization:
import { virgilCrypto } from 'react-native-virgil-crypto';
import createNativeKeyEntryStorage from '@virgilsecurity/key-storage-rn/native';
import { VirgilCardCrypto, VirgilPrivateKeyExporter } from '@virgilsecurity/sdk-crypto';
import { CachingJwtProvider, CardManager, VirgilCardVerifier, PrivateKeyStorage } from 'virgil-sdk';
const jwtProvider = new CachingJwtProvider(getTokenFunctionDefinedInYourCode);
const cardCrypto = new VirgilCardCrypto(virgilCrypto);
const cardVerifier = new VirgilCardVerifier(cardCrypto);
const cardManager = new CardManager({
cardCrypto,
cardVerifier,
accessTokenProvider: jwtProvider,
retryOnUnauthorized: true,
});
const privateKeyStroage = new PrivateKeyStorage(
new VirgilPrivateKeyExporter(virgilCrypto),
createNativeKeyEntryStorage({ username: nameOfTheStorageChosenByYou });
);
// use `cardManager` to work with cards
// use `privateKeyStorage` to save and retrieve private keys to\from local storage
$ npm install react-native-virgil-crypto --save
This library depends on the native virgil-crypto-x project. To keep the React Native library agnostic of your dependency management mehtod, the native libraries are not distributed as part of the bridge.
VirgilCrypto supports two options for dependency management:
-
CocoaPods
RN<0.60
react-native link react-native-virgil-crypto
Add the following lines to your
Podfile
:pod 'VirgilCrypto', '~> 5.2.0' pod 'VirgilCryptoFoundation', '~> 0.11.0' pod 'VirgilCryptoPythia', '~> 0.11.0'
Make sure you have
use_frameworks!
there as well. Then runpod install
from inside theios
directory.RN=0.60.x
Unfortunately, this library cannot be used with CocoaPods and React Native 0.60 since it does not support
use_frameworks!
. The recommended option is to install the native dependency with Carthage and link the bridge (i.e. this library) manually.⚠️ Since version 0.4.0 of this library, you need to explicitly disable autolinking on iOS for this libary. To do that, you'll need to update yourreact-native.config.js
'sdependencies
entry to look like this (create this file in the root of your project if you don't have it):module.exports = { dependencies: { 'react-native-virgil-crypto': { platforms: { ios: null // disable iOS platform, other platforms will still autolink if provided } } } };
RN>0.61
With React Native > 0.61 linking is done automatically. You just need to make sure you have
use_frameworks!
in yourPodfile
and you runpod install
inside theios
folder after installing from npm. -
Carthage With Carthage add the following line to your
Cartfile
:github "VirgilSecurity/virgil-crypto-x" ~> 5.2.0
Then run
carthage update --platform iOS
from inside theios
folder.On your application target's “General” settings tab, in the “Linked Frameworks and Libraries” section, add following frameworks from the Carthage/Build folder inside your project's folder:
- VirgilCrypto
- VirgilCryptoFoundation
- VirgilCryptoPythia
- VSCCommon
- VSCFoundation
- VSCPythia
On your application target's “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script in which you specify your shell (ex: /bin/sh), add the following contents to the script area below the shell:
/usr/local/bin/carthage copy-frameworks
and add the paths to the frameworks you want to use under “Input Files”, e.g.:
$(SRCROOT)/Carthage/Build/iOS/VirgilCrypto.framework $(SRCROOT)/Carthage/Build/iOS/VirgilCryptoFoundation.framework $(SRCROOT)/Carthage/Build/iOS/VirgilCryptoPythia.framework $(SRCROOT)/Carthage/Build/iOS/VSCCommon.framework $(SRCROOT)/Carthage/Build/iOS/VSCFoundation.framework $(SRCROOT)/Carthage/Build/iOS/VSCPythia.framework
Important! You will have to set
minSdkVersion
to at least21
in your project'sbuild.gradle
file to use this library.
RN<0.60
react-native link react-native-virgil-crypto
With React Native 0.60 and later, linking is done automatically.
- In XCode, in the project navigator, right click
Libraries
➜Add Files to [your project's name]
- Go to
node_modules
➜react-native-virgil-crypto
and addRNVirgilCrypto.xcodeproj
- In XCode, in the project navigator, select your project. Add
libRNVirgilCrypto.a
to your project'sBuild Phases
➜Link Binary With Libraries
- Run your project (
Cmd+R
)<
- Open up
android/app/src/main/java/[...]/MainApplication.java
- Add
import com.virgilsecurity.rn.crypto.RNVirgilCryptoPackage;
to the imports at the top of the file - Add
new RNVirgilCryptoPackage()
to the list returned by thegetPackages()
method
- Append the following lines to
android/settings.gradle
:include ':react-native-virgil-crypto' project(':react-native-virgil-crypto').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-virgil-crypto/android')
- Insert the following lines inside the dependencies block in
android/app/build.gradle
:compile project(':react-native-virgil-crypto')
This library is released under the 3-clause BSD License.
Our developer support team is here to help you.
You can find us on Twitter or send us email support@VirgilSecurity.com.
Also, get extra help from our support team on Slack.