BlackBerry Spark Communications Services Guide

Security

BlackBerry Spark Communications Services security model is strong. To access the service, users and endpoints must provide proof of their identity that the BlackBerry Infrastructure will verify against your choice of identity provider. Even after those endpoints have access to the service, only other users and endpoints can grant them access to your application's chats and data.

The SDK takes care of the security details for you so you can focus on building your application.

Feature Description
Encryption The SDK encrypts the messages that it sends. This ensures that only the intended recipients can read it.
Signing and Verification The SDK signs the messages that it sends. This ensures that recipients can prove who sent them the message because the SDK also verifies the signatures of received messages.
Data Protection The SDK protects any persistent copies of received messages by storing them in an encrypted database. This ensures that your data remains safe in the face of on-device inspection.
Key Management The SDK exchanges keys securely so that your users have trustable encryption and signing keys to perform these checks and create secure relationships between user identities.
Strong Hashing The SDK uses strong hashing algorithms including SHA-512 and Argon2id (winner of the Password Hashing Competition) where appropriate. This ensures integrity checks are robust against collision and brute force attacks.
Encrypted Calls and Peer-to-Peer Data Transfer The SDK securely establishes encrypted calls for voice, video, and data streams using those relationships. This ensures that even real-time, high-bandwidth communication between endpoints is secure.

Key Types

The SDK uses two types of cryptographic keys for most communications between application endpoints: identity keys and chat keys.

Each user of your application is assigned a Spark Communications Services identity. Each identity is represented cryptographically by a set of public-private identity key pairs. One of the key pairs in this set is used to sign messages from the identity, and one is used to create secure peer-to-peer encryption contexts between two identities. The public identity keys must be shared with other identities, while the private identity keys must only be held by the endpoints of the identity that owns them.

Each chat has a chat key that is used to protect the metadata and messages of that chat. All participant endpoints within a chat must share this key, and it must be protected from identities and endpoints that do not belong to the chat.

BlackBerry Key Management Service

Secure key exchange is an essential part of establishing trust. Each endpoint of your application must know that the keys it has for itself and for other endpoints are trustworthy and represent the correct identity.

The BlackBerry Spark Communications Services SDK has built-in support for the BlackBerry Key Management Service, a complete cloud-based solution that automatically stores and distributes keys between your application's endpoints without any effort on your part.

The BlackBerry Key Management Service (KMS) is a cloud-based solution that stores and shares keys used to secure messages plus voice, video, and data calls.

KMS Architecture

Sharing Public Keys

KMS shares an identity's public encryption and signing keys with all of your application's other endpoints. Any identity within your application's domain can read these keys. Only the owning identity can write them.

Storing Private Keys

KMS stores an identity's own private identity and chat keys so that they can be restored by any endpoints that are owned by that same identity. The SDK encrypts and signs all private identity and chat keys with an AES256-GCM Management Key before storing them in KMS. The SDK also encrypts and signs that management key with a separate AES256-GCM Derived Root Key and stores it in KMS. That derived key is generated with the Argon2id key derivation function from a secret that your application provides during endpoint setup.

KMS: Publishing Keys

The shared secret that protects your application's keys can be a user-supplied password or a secret managed by your application on behalf of your user. The Derived Root Key that is generated from this secret is never stored in KMS or on the endpoints. This means that BlackBerry can not read or modify the keys stored in KMS.

Restoring Private Keys

When a new endpoint is created for an identity, the SDK automatically restores its private keys from KMS. During endpoint setup, your application must provide the SDK with the secret that is protecting the keys. The SDK derives the Derived Root Key from that secret and uses that key to verify and decrypt the Management Key that it restores from KMS. The SDK then uses the Management Key to verify and decrypt all of the identity's private keys.

KMS: Restoring Keys

Changing the KMS Secret

Your application can change the secret that is used to protect its keys in KMS. Because it uses both a Derived Root Key and a Management Key, KMS lets your application change the secret that is used to protect the securely stored keys without requiring re-encryption of all those keys. The SDK only needs to re-encrypt the Management Key with a new Derived Root Key.

This means your application can change the secret that protects its keys at any time without complication.

Losing the KMS Secret

When a new endpoint is added to an identity, it needs to restore keys from KMS. If your user or your application loses access to the secret that was used to protect the private keys stored in KMS, all stored private keys are lost. When your application tells the SDK that it cannot provide the KMS secret, the SDK automatically generates new public-private key pairs for its identity.

The SDK publishes the identity's new public keys to KMS and saves its new private keys to KMS with a new secret, a new Derived Root Key, and a new Management Key. All other endpoints of this same identity are automatically deregistered and they wipe all SDK data. Endpoints of other identities re-import the new public keys as needed.

The SDK will automatically attempt to restore lost chat keys from other chat participants using internal mechanisms based on cryptographic proof that the affected identity was a member of the chat. You can chose to have your application leave some or all chats during or after this process. Other identities can re-invite the affected identity to chats it has left.

Examples

All of the SDK example applications support KMS. It is the default mode of operation for most examples.

Cloud Key Storage

Your application can take complete control over its keys with the SDK's key import and export APIs. You can apply your own protection mechanisms and share the keys under your own rules. You can use your own storage solution, or integrate with cloud storage providers like Firebase or Azure Cosmos DB, for example.

More information about how to use Cloud Key Storage is available.

Several of the SDK example applications demonstrate how to use Cloud Key Storage.

Cryptography

The SDK will generate two private and public key pairs for each identity, using ECDSA secp521r1: one key pair for signing and one key pair for encryption. The public signing and encryption keys are distributed to endpoints via the BlackBerry Key Management Service or your own Cloud Key Storage.

All sent messages are encrypted using a per-message key, and signed by the sender's private signing key. All received messages are verified using the sender's public signing key, and decrypted using a per-message key. The per-message keys are generated differently for identity and chat messages and are described below.

In order for your application to function, exported cryptographic keys must be stored and distributed using the BlackBerry Key Management Service or your own Cloud Key Storage.

Identity Messages

Identity messages are messages exchanged between two identities outside of a chat (for instance, an invitation to join a chat from one identity to another). Identity messages are encrypted using a per-message key generated by both the sender and recipient: the remote identity's public encryption key and the local identity's private encryption key are used to generate a ECDH secp521r1 528-bit shared secret. This shared secret is combined with the message counter and nonce to make a secret that is used to derive a 32-byte key using ANSI-X9.63-KDF.

The message counter is incremented for each message and the nonce is randomly generated for each message. These values are sent in the plaintext portion of the protected message.

The derived key is used to encrypt/decrypt the identity message using AES CTR mode.

Mathematical Notation

A calculates K_shared = EC-DH(K’enc_A, Kenc_B)

B calculates K_shared = EC-DH(K’enc_B, Kenc_A)

Headers = Nonce (64-bit) || Counter (32-bit) || <other_message_headers>

K_msg = KDF(Headers || K_shared)

Message = Enc_{K_msg, Nonce}(Payload)

Chat Messages

Chat messages are encrypted using a per-message key generated by both the sender and recipient. The chat key is sent to the identity as a protected identity message, requesting that the user to join the chat. The chat key is shared via an invite that is sent as a protected identity message for the identity to join the chat. The chat key is combined with the message counter and nonce to make the secret used to derive a 32-byte key using ANSI-X9.63-KDF.

The message counter is incremented for each message, while the nonce is randomly generated for each message. These values are sent in the plaintext portion of the protected message.

The derived key is used to encrypt/decrypt the message using AES CTR mode.

Mathematical Notation

Headers = Nonce (64-bit) || Counter (32-bit) || <other_message_headers>

K_msg = KDF(Headers || K_Session)

Message = Enc_{K_msg, Nonce}(Payload)

Signing

Before identity and chat messages are signed, the encrypted data is combined with the message counter, nonce, and identity information. It is then signed with the sender's private signing key, using ECDSA secp521r1 SHA-512. The signature information is sent in the plaintext portion of the protected message, to be verified by the recipient. The recipient combines the received encrypted data, message counter and nonce, and identity information to verify it using ECDSA secp521r1 SHA-512 with the sender's public signing key.

Mathematical Notation

Sent Message: Headers || Message || Sign_{Ksign_sender}(Hash( Headers || Message ))

Media and Data Streams

The SDK provides end-to-end security for a real-time peer-to-peer video, voice and data streams, allowing endpoints to place secure voice or video calls or share data. Encryption keys are exchanged using DTLS-SRTP and are used to secure the SRTP stream of media or the SCTP stream of data. Additionally, DTLS fingerprints are encrypted and signed using the identity keys of both participants in the video, voice call or data sharing session, adding another layer of security and identity verification. Subsequent encryption of the real-time media stream and peer-to-peer stream follows SRTP specification, using AES-128 in CTR mode or, in the most recent versions, AES-256 in GCM mode as a cipher.

WebCrypto

WebCrypto is a standards-track proposal to include cryptographic primitives in the browser, and has all primitives needed for the Web version of the SDK for JavaScript. WebCrypto has not yet been standardized. Only some browsers support the standards-track version of WebCrypto. Some other browsers have a pre-standards-track version of WebCrypto still implemented in native code but with a slightly different interface. Some browsers have neither.

Primitives

The following WebCrypto primitives are used by the SDK for JavaScript:

Generate Signing Key Pair

crypto.subtle.generateKey({ name: 'ECDSA', namedCurve: 'P-521' }, true, ['sign', 'verify']);

Generate Encryption Key Pair

crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-521' }, true, ['deriveBits']);

Generate Shared Secret

crypto.subtle.deriveBits({ name: 'ECDH', namedCurve: 'P-521', public: publicKey }, privateKey, 528);

Sign

crypto.subtle.sign({ name: 'ECDSA', hash: { name: 'SHA-512' } }, importedKey, buildSignData(prefix, data));

Verify

crypto.subtle.verify({ name: 'ECDSA', hash: { name: 'SHA-512' } }, importedKey, sign, buildSignData(prefix, data));

Encrypt

crypto.subtle.encrypt(
  {
    name: 'AES-CTR',
    counter: buildInitVector(nonce),
    length: 128
  },
  derivedKey, plaintext
);

Decrypt

crypto.subtle.decrypt(
  {
    name: 'AES-CTR',
    counter: buildInitVector(nonce),
    length: 128
  },
  derivedKey, ciphertext
);

Diagrams

Encryption and Signing

Encryption and Signing

Data flow: Sending an encrypted and signed message

Encryption and Signing Encryption

When an endpoint sends a message, the SDK performs the following actions.

  1. Establishes a 256-bit AES message key from the session key and unique keying material
  2. Encrypts the message with the symmetric key using AES in CTR mode
  3. Includes the keying material to recreate the message key in the unencrypted portion of the message
  4. Hashes the whole message using SHA-512
  5. Signs the hash with the sender’s private signing key (ECC-521) using ECDSA
  6. Wraps the parts in a message envelope
  7. Passes the message to the transport layer

Data flow: Decrypting and verifying a received message

Decryption and Signing

When an endpoint receives a message, the SDK performs the following actions:

  1. Parses the envelope containing the encrypted message
  2. Hashes the encrypted message using SHA-512
  3. Verifies the message signature using the sender’s public key and the encrypted message hash; a pass indicates that the message is authentic
  4. Derives the message key from the session key and the unencrypted keying material
  5. Decrypts the message using AES in CTR mode

End-to-End View

End-to-End View