Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
sss.openstar.message.payloads.MessageEcryption.scala Maven / Gradle / Ivy
package sss.openstar.message.payloads
import java.util
import akka.util.ByteString
import scorex.crypto.signatures.SharedSecret
import sss.ancillary.FailWithException.fail
import sss.ancillary.{Guid, Logging}
import sss.openstar.account.{NodeIdTag, NodeIdentity, NodeVerifier}
import sss.openstar.balanceledger._
import sss.openstar.crypto.AESDetails.AESEncodedKey
import sss.openstar.crypto.CBCEncryption.InitVector
import sss.openstar.crypto.{AESDetails, CBCEncryption, SeedBytes}
import sss.openstar.identityledger.IdentityServiceQuery
import sss.openstar.message.payloads.ThreadedMessage.{Threadable, Threaded}
import sss.openstar.message.{BodyToBeEncryptedFromBytes, MessageComposite, MessageUpdater}
import sss.openstar.util.ThrowUtil.throwRuntime
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success, Try}
/**
* Created by alan on 6/28/16.
*/
object MessageEcryption {
case class EmbeddedBounty(
amountOfBounty: Long,
bountyReturnBlockHeight: Long,
addrOfBounty: TxIndex,
)
case class EncryptedMessage(
bountyOpt: Option[EmbeddedBounty],
author: NodeIdTag,
receivers: Seq[EncryptedSessionKey],
encrypted: ByteString,
iv: InitVector,
guid: Guid,
childGuids: Seq[Guid]
) extends MessageComposite
with Threadable
with Threaded
with UtxoWatcher
with Logging {
override def apply[C >: MessageComposite](m: MessageUpdater): C = m match {
case t: Threadable if !childGuids.contains(t.guid) =>
this.copy(childGuids = childGuids :+ t.guid)
case _: Threadable =>
this
case x =>
log.warn(s"Cannot add $x to Encrypted Msg")
this
}
override def watch: Seq[TxIndex] = {
bountyOpt.map(bounty =>
receivers
.indices
.map(
i => bounty.addrOfBounty.copy(index = bounty.addrOfBounty.index + i)
)
).getOrElse(Seq.empty)
}
def decrypt(receiver: NodeIdentity, lookup: NodeIdTag => NodeVerifier)(implicit ec: ExecutionContext): Future[BodyToBeEncrypted] = {
val (receiverData, sendPublicKeyAccount) = (if (receiver.id == author.nodeId) {
receivers.find(_.identity.nodeId != author.nodeId) map { found =>
val verifier = lookup(found.identity)
(found, verifier)
}
} else {
receivers.find(_.identity.nodeId == receiver.id) map { found =>
(found, lookup(author))
}
}).getOrElse(
fail(s"Receiver ${receiver.id} is not in the recipient list")
)
val sharedSecretF: Future[SharedSecret] = receiver
.defaultNodeVerifier
.signer
.createSharedSecret(sendPublicKeyAccount.verifier.typedPublicKey)
sharedSecretF.map { sharedSecret =>
val decryptedSessionKey = Try(CBCEncryption.decrypt(
AESDetails.aesKeyFromByteArray(sharedSecret),
receiverData.encryptedSessionKey.toArray,
CBCEncryption.initVector(receiverData.iv.toArray)
)) match {
case Success(s) => AESEncodedKey(s)
case Failure(exception) =>
log.warn(s"REC ${receiver.id} author $author recdata ${receiverData.identity}")
throw exception
}
val decryptedMessage = CBCEncryption.decrypt(
decryptedSessionKey,
encrypted.toArray,
iv
)
decryptedMessage.toBodyToBeEncrypted
}
}
}
case class AttachmentDetails(name: String, mimeType: String, size: Long)
case class MessageAttachments(attachmentProvider: String, attachmentDetails: Seq[AttachmentDetails] = List.empty,
key: AESEncodedKey = AESDetails.generateAESKey())
case class EncryptedSessionKey(
identity: NodeIdTag,
iv: ByteString,
encryptedSessionKey: ByteString) {
require(iv.size >= 16, s"128 bit key minimum size, ${iv.size} not allowed")
}
case class BodyToBeEncrypted(
text: String,
secret: Array[Byte],
attachmentsOpt: Option[MessageAttachments]
)
def encryptWithEmbeddedSecret(
amountOfBounty: Long,
bountyReturnBlockHeight: Long,
addrOfBounty: TxIndex,
sender: NodeIdentity,
receivers: Seq[NodeVerifier],
text: String,
attachments: Option[MessageAttachments],
secret: Array[Byte],
guid: Guid,
guids: Seq[Guid]
)(implicit ec: ExecutionContext): Future[EncryptedMessage] =
encryptWithEmbeddedSecret(
Some(EmbeddedBounty(amountOfBounty, bountyReturnBlockHeight, addrOfBounty)),
sender,
receivers,
text,
attachments,
secret,
guid,
guids
)
def encryptWithEmbeddedSecret(
bountyOpt: Option[EmbeddedBounty],
sender: NodeIdentity,
receivers: Seq[NodeVerifier],
text: String,
attachments: Option[MessageAttachments],
secret: Array[Byte],
guid: Guid,
guids: Seq[Guid]
)(implicit ec: ExecutionContext): Future[EncryptedMessage] = {
val sessionKey = AESDetails.generateAESKey()
val receiversEncryptedSessionKeys = receivers map { receiver =>
val signer = sender.defaultNodeVerifier.signer
val sharedSecretF: Future[SharedSecret] = signer.createSharedSecret(receiver.verifier.typedPublicKey)
val iv = CBCEncryption.newInitVector(SeedBytes)
sharedSecretF.map { sharedSecret =>
val aesSessionKey = AESDetails.aesKeyFromByteArray(sharedSecret)
EncryptedSessionKey(receiver.nodeIdTag,
ByteString(iv.bytes),
ByteString(CBCEncryption.encrypt(aesSessionKey, sessionKey.value, iv))
)
}
}
val seqEncSessKeysF: Future[Seq[EncryptedSessionKey]] = Future.sequence(receiversEncryptedSessionKeys)
val iv = CBCEncryption.newInitVector(SeedBytes)
val bytes: Array[Byte] = BodyToBeEncrypted(text, secret, attachments).toBytes
val encryptedMessage = CBCEncryption.encrypt(sessionKey, bytes, iv)
seqEncSessKeysF.map { receiversEncryptedSessionKeys =>
EncryptedMessage(
bountyOpt,
NodeIdTag(sender.id, sender.tag),
receiversEncryptedSessionKeys,
ByteString(encryptedMessage),
iv,
guid,
guids
)
}
}
def createLookupId(identityServiceQuery: IdentityServiceQuery): NodeIdTag => NodeVerifier =
nId =>
identityServiceQuery
.toVerifier(
identityServiceQuery
.accountOpt(nId.nodeId, nId.tag)
.getOrElse(fail(s"No such user ${nId.nodeId}")
)
)
}