
Research
Security News
Malicious PyPI Package Exploits Deezer API for Coordinated Music Piracy
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
@consento/crypto
Advanced tools
@consento/crypto
is a set of crypto primitives useful for the communication within the
consento workflow.
Pre 1.0. This library is under heavy development. There are still updates to both the interna of this library and to namings.
libsodium
is a good crypto foundation to build on but
using it as-is presents various issues:
keys
are all of the same type (Uint8Array
) and its easy to mistake one key for another one.
This library is set out to create a good dictionary and structure on top of libsodium
that
enables users to clearly understand what happens where. This is achived through proper naming: for example:
we distinguish verifyKey
and decryptKey
which would both be called pk
in libsodium
. This
extends to the type definition which are as expressive as possible.
Users need to know both the
key
type and the implementation to use it which makes it prone to type errors.
In this library we have structures like Writer
that keep the encryptKey
needed for a encryption
operation
making an API like writer.encrypt('message')
possible, which natively would look like
crypto_box_seal(encryptKey, stringToBuffer('message'))
.
libsodium
works withUint8Array
but users haveObjects
.
Any data that is processed with libsodium
is binary, which gives is flexibly but not how it's used in practice.
With this library comes codec
support for all structures. You can specify for a read
and write
process to
use complex data structures, even supporting custom structures if you need those. Core strategy here is to specify
the types to a great detail with TypeScript. For example, the Writer
has a generic information for the codec:
Writer<typeof codecs.msgpack>
which means you can see while coding what data the Writer
can encode anything
that is encodable with msgpack.
The de-/serialization of structures using
libsodium
can be tedious.
Everything in this library can be serialized with .toJSON()
and restored with the constructor. Example:
new Writer(writer.toJSON())
. This allows for an easy means to preserve the data structure in a standard format.
A goal for 2.0.0
is to provide protocol buffer de-/encoding to allow more efficent data structures.
Channel
- an e2e encrypted setup consisting of one Reader
and one Writer
Writer
- an object containing the keys that allow to encrypt data - it can write on that Channel - it can not read the data!Reader
- an object containing the keys that allow to decrypt data - it can read on that Channel - it can not write to it!Verifier
- an object describing an the capability to verify if a message is part of a Channel
without being able to read it.Connection
- an e2e encrypted setup consisting of the Reader
of one channel, called input
and the Writer
of another, called output
.Blob
- a self-contained piece of data, like an image or pdf.Handshake
- the process to connect two separate processes/devices resulting in a Connection
for each process.SignVector
- operations on a Channel
may be vectored
with means that there is a new sign/verify keypair for every new message.
The SignVector
holds the index
and current sign
or verify
key.VerifyVector
- sibling to SignVector
the VerifyVector
allows to verify things in the order that the SignVector
created it.Codec
- Data written by a reader or read by a writer will be transported binary (Uint8Array
), a Codec
specifies how an object read
or written will be translated from/to binary data.The crypto library contains useful primitives for sending e2e encrypted messages through public channels.
const { createChannel } = require('@consento/crypto')
const { writer, reader } = createChannel()
const encrypted = writer.encrypt('hello world')
const decrypted = reader.decrypt(encrypted) === 'hello world'
You can create a new communication channel with the simple createChannel
method.
const channel = createChannel()
const { reader } = channel // Can decrypt messages; _could_ encrypt messages, but these would not be signed and rejected!
const { writer } = channel // Can only encrypt messages.
const { verifier } = channel // Object that can verify messages but not de-/encrypt messages.
reader.readerKey // To backup/restore the receiver
writer.senderKey // To backup/restore the sender
reader.verifyKeyBase64 === writer.verifyKeyBase64 === verifier.verifyKeyBase64
// The lookup id is same here, the verifyKey may be used to verify the data, can also be used for the channel
writer.signKey // Allows the writer to sign messages, only privy to the writer
reader.decryptKey // Allows the reader to decrypt messages, only privy to the reader
writer.encryptKey.equals(receiver.encryptKey) // Key to encrypt messages, receiver _can_ write but not sign the message, thus it exists pro-forma
All objects created using createChannel
are well de-/serializable:
const { createChannel, Reader, Writer, Verifier } = require('@consento/crypto')
const channel = createChannel()
const { reader, writer, verifier } = channel
new Channel(channel.toJSON())
new Reader(reader.toJSON())
new Writer(writer.toJSON())
new Verifier(verifier.toJSON())
Any data sent out through Writer
s or Reader
s is encoded using mechanism, by default it will be using msgpack
but you can specify any codec supported by @consento/codecs
.
const { createChannel } = require('@consento/crypto')
const { writer } = createChannel({ codec: 'json' }) //
writer.encrypt({ foo: 'hello' }) // Changes the binary format to be utf-8 encoded JSON data.
const differentCodec = new Writer({ ...writer.toJSON(), codec: 'msgpack' })
differentCodec.encrypt({ foo: 'hello' }) // Looks same but the binary data is now encoded using msgpack
The encrypt
, decrypt
and verify
operations can be extended using a SignVector
. The SignVector
allows for all operations to be in sequential order. In other words: the chunks need to be decrypted/verified
in the same order as they were encrypted.
const { createChannel, createSignVectors, SignVector } = require('@consento/crypto')
const { inVector, outVector } = createSignVectors()
const { writer, reader, verifier } = createChannel()
const list = []
list.push(writer.encrypt('foo', outVector))
console.log(outVector)
list.push(writer.encrypt('bar', outVector))
console.log(outVector)
list.push(writer.encrypt('baz', outVector))
console.log(outVector)
const decryptVector = new SignVector(inVector)
for (const entry of list) {
console.log(reader.decrypt(entry, decryptVector))
console.log(decryptVector)
}
const verifyVector = new SignVector(inVector)
for (const entry of list) {
try {
verifier.verify(entry, verifyVector)
} catch (err) {
// not verified
}
console.log(verifyVector)
}
Encrypt and sign a given input with the encryptKey
and signKey
.
body
- what you like to encrypt, any serializable object is possiblesignVector
- optional SignVector
instance to assure order of statements.
See Sign Vectors.const encrypted = writer.encrypt('secret message')
encrypted.signature // Uint8Array
encrypted.body // Uint8Array
Only encrypt the body. This is only recommended in an environment where the signature needs to be created at a different time!
body
- what you like to encrypt, any serializable object is possiblesignVector
- optional SignVector
instance to assure order of statements.
See Sign Vectors.const encrypted = writer.encrypt('secret message')
encrypted // Uint8Array with an encrypted message
Signs a message in the order received.
message
- an Uint8Array
that should be signed.const { outVector } = createSignVectors()
outVector.sign('hello world')
outVector.sign('hello world') // Different signature!
Verifies that a message is encrypted with the corresponding signVector
created using createSignVectors
message
- an Uint8Array
with the message for the signaturesignature
- an Uint8Array
that contains the signatureconst { EDecryptionError } = require('@consento/crypto')
const { inVector } = createSignVectors()
try {
inVector.verify(message, signature)
} catch (error) {
switch (error.code) {
case EDecryptionError.unexpectedIndex: // Order of messages may be wrong
case EDecryptionError.vectorIntegrity: // General vector verificaton failed
}
}
Signs a given data. This is only recommended in an environment where the data was encrypted at a different time!
data
- Uint8Array for which a signature is wantedconst signature = sender.sign(sender.encryptOnly('secret message'))
signature // Uint8Array with the signature of the encrypted message
Using the annonymous object we can verify a given data.
signature
- Uint8Array
with the signature for the body
body
- Uint8Array
with of the encrypted data.signVector
- optional SignVector
instance to assure order of statements.
See Sign Vectors.const encrypted = writer.encrypt('hello world')
try {
verifier.verify(encrypted.signature, encrypted.body)
} catch (err) {
switch (err.code) {
case EDecryptionError.invalidSignature: // Signature doesn't match
case EDecryptionError.unexpectedIndex: // Order of messages may be wrong, only with SignVector
case EDecryptionError.vectorIntegrity: // General vector verificaton failed, only with SignVector
}
}
As a short-cut its also possible to just verify a message
message
- { signature: Uint8Array, body: Uint8Array }
can also be Uint8Array
in combination with a
signVector
signVector
- optional SignVector
instance to assure order of statements.
See Sign Vectors.try {
verifier.verifyMessage(message)
} catch (err) {
switch (err.code) {
case EDecryptionError.invalidSignature: // Signature doesn't match
case EDecryptionError.unexpectedIndex: // Order of messages may be wrong, only with SignVector
case EDecryptionError.vectorIntegrity: // General vector verificaton failed, only with SignVector
}
}
Get the content of a once encrypted message.
encrypted
- { signature: Uint8Array, body: Uint8Array }
as created by writer.encrypt
or
Uint8Array
created with writer.encryptOnly
signVector
- optional SignVector
instance to assure order of statements.
See Sign Vectors.const message = reader.decrypt(message:)
crypto
also holds primitives for a decentralized handshake mechanism.
const { initHandshake, acceptHandshake } = require('@consento/crypto')
initHandshake
is to be used by the first person - "Alice".
acceptHandshake
is to be used by the second person - "Bob".
How the handshake works:
Alice needs to create the initial message:
const alice = initHandshake()
const initMessage = alice.firstMessage
Alice needs to listen to the channel with the id alice.receiver.id
for answers that may come from Bob.
Alice needs to send hand the initial message to Bob using any means. (QR Code, Email,...)
Bob needs to receive the initial message
const bob = acceptHandshake(firstMessage)
Bob needs to listen to the channel with the id bob.receiver.id
for the final message from Alice.
Bob needs to send the message, encrypted to the channel with the id: bob.sender.id
:
bob.sender.encrypt(bob.acceptMessage)
Alice has to receive the acception message and can generate the channels out of it.
const decryptedAcceptMessage = alice.receiver.decryptMessage(acceptMessage).body
const package = confirmHandshake(alice, decryptedAcceptMessage)
const {
connection: {
sender: aliceToBobSender, // channel to send messages to Bob
receiver: bobToAliceReceiver, // channel to receive messages from Bob
},
finalMessage
} = package
Alice has to send the final message to bob:
aliceToBobSender.encrypt(finalMessage)
Bob can now finalize the handshake
const { output: bobToAliceOutput, input: aliceToBobInput } = bob.finalize(finalMessage)
Now Alice and Bob have each two channels: one to send data to, one to receive data from.
bobToAliceReceiver.decrypt(aliceToBobSender.encrypt('Hello Bob!')) // Hello Bob!
aliceToBobReceiver.decrypt(bobToAliceSender.encrypt('Hello Alice!')) // Hello Alice!
This a all-in-one API to encode, encrypt and store binary data and allow to easily encrypt it.
Only one key is necessary to find and decrypt previously encrypted data!
const { encryptBlob, isEncryptedBlob } = require('@consento/crypto')
const {
blob, // Information about a blob: to pass around
encrypted // Encrypted data to be stored
} = encryptBlob('Hello Secret!', 'utf8')
blob.locationKey // Key to locate an object for that secret
blob.path // Path at which to store the encrypted data, useful for file systems
blob.secretKey // Secretkey to decrypt this data
blob.size // Number of bytes of the encrypted blob (optional information)
isEncryptedBlob(blob) // To verify if a set of data is a blob
const decrypted = blob.decrypt(encrypted)
Blob information is serializable with toJSON
and deserializable using the EncryptedBlob
class:
const { blob } = encryptBlob('Hello Secret!', 'utf-8')
const blobJSON = blob.toJSON()
const sameBlob = new EncryptedBlob(blobJSON)
Blob storage is binary by default, if you intend to use encode other types of data use the
codec
option.
const { blob, encrypted } = encryptBlob({ hello: 'world' }, 'msgpack')
It is possible to restore a blob from it's secretKey
alone, this
may require you to pass-in a codec.
const { encryptBlob, toEncryptedBlob } = require('@consento/crypto')
const { blob } = encryptBlob('Hello Secret!')
const sameBlob = toEncryptedBlob(blob.secretKey, blob.codec.name)
FAQs
Crypto functionality used in Consento
The npm package @consento/crypto receives a total of 38 weekly downloads. As such, @consento/crypto popularity was classified as not popular.
We found that @consento/crypto demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
Research
The Socket Research Team discovered a malicious npm package, '@ton-wallet/create', stealing cryptocurrency wallet keys from developers and users in the TON ecosystem.
Security News
Newly introduced telemetry in devenv 1.4 sparked a backlash over privacy concerns, leading to the removal of its AI-powered feature after strong community pushback.