Use encrypt-then-mac instead of mac-and-encrypt.
This commit is contained in:
parent
9b5175114f
commit
925eb2d5c5
4 changed files with 35 additions and 32 deletions
|
@ -63,7 +63,7 @@ class ConnectionHandler(settings: Settings, database: DatabaseInterface,
|
|||
settings.put("message_id", messageId + 1)
|
||||
|
||||
val msg = new Message(header, body)
|
||||
val encrypted = crypto.encrypt(crypto.sign(msg))
|
||||
val encrypted = crypto.encryptAndSign(msg)
|
||||
router.onReceive(encrypted)
|
||||
onNewMessage(msg)
|
||||
}
|
||||
|
@ -77,12 +77,12 @@ class ConnectionHandler(settings: Settings, database: DatabaseInterface,
|
|||
*/
|
||||
def onMessageReceived(msg: Message): Unit = {
|
||||
if (msg.header.target == crypto.localAddress) {
|
||||
val decrypted = crypto.decrypt(msg)
|
||||
if (!crypto.verify(decrypted)) {
|
||||
Log.i(Tag, "Ignoring message with invalid signature from " + msg.header.origin)
|
||||
return
|
||||
crypto.verifyAndDecrypt(msg) match {
|
||||
case Some(msg) => onNewMessage(msg)
|
||||
case None =>
|
||||
Log.i(Tag, "Ignoring message with invalid signature from " + msg.header.origin)
|
||||
return
|
||||
}
|
||||
onNewMessage(decrypted)
|
||||
} else {
|
||||
router.onReceive(msg)
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ class ConnectionHandler(settings: Settings, database: DatabaseInterface,
|
|||
return false
|
||||
}
|
||||
|
||||
if (crypto.havePublicKey(sender) && !crypto.verify(msg, crypto.getPublicKey(sender))) {
|
||||
if (crypto.havePublicKey(sender) && !crypto.verify(msg, Option(crypto.getPublicKey(sender)))) {
|
||||
Log.i(Tag, "Ignoring ConnectionInfo message with invalid signature")
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -135,15 +135,13 @@ class Crypto(settings: Settings, keyFolder: File) {
|
|||
val key = loadKey(PrivateKeyAlias, classOf[PrivateKey])
|
||||
sig.initSign(key)
|
||||
sig.update(msg.body.write)
|
||||
new Message(msg.header, new CryptoData(Option(sig.sign), None), msg.body)
|
||||
new Message(msg.header, new CryptoData(Option(sig.sign), msg.crypto.key), msg.body)
|
||||
}
|
||||
|
||||
def verify(msg: Message, key: PublicKey = null): Boolean = {
|
||||
val publicKey =
|
||||
if (key != null) key
|
||||
else loadKey(msg.header.origin.toString, classOf[PublicKey])
|
||||
def verify(msg: Message, key: Option[PublicKey] = None): Boolean = {
|
||||
val sig = Signature.getInstance(SigningAlgorithm)
|
||||
sig.initVerify(publicKey)
|
||||
lazy val defaultKey = loadKey(msg.header.origin.toString, classOf[PublicKey])
|
||||
sig.initVerify(key.getOrElse(defaultKey))
|
||||
sig.update(msg.body.write)
|
||||
sig.verify(msg.crypto.signature.get)
|
||||
}
|
||||
|
@ -223,9 +221,18 @@ class Crypto(settings: Settings, keyFolder: File) {
|
|||
}
|
||||
}
|
||||
|
||||
def encrypt(msg: Message, key: PublicKey = null): Message = {
|
||||
assert(msg.crypto.signature.isDefined, "Message must be signed before encryption")
|
||||
def encryptAndSign(msg: Message, key: Option[PublicKey] = None): Message = {
|
||||
sign(encrypt(msg, key))
|
||||
}
|
||||
|
||||
def verifyAndDecrypt(msg: Message, key: Option[PublicKey] = None): Option[Message] = {
|
||||
if (verify(msg, key))
|
||||
Option(decrypt(msg))
|
||||
else
|
||||
None
|
||||
}
|
||||
|
||||
private def encrypt(msg: Message, key: Option[PublicKey] = None): Message = {
|
||||
// Symmetric encryption of data
|
||||
val secretKey = makeSecretKey()
|
||||
val symmetricCipher = Cipher.getInstance(SymmetricKeyAlgorithm)
|
||||
|
@ -233,17 +240,15 @@ class Crypto(settings: Settings, keyFolder: File) {
|
|||
val encrypted = new EncryptedBody(copyThroughCipher(symmetricCipher, msg.body.write))
|
||||
|
||||
// Asymmetric encryption of secret key
|
||||
val publicKey =
|
||||
if (key != null) key
|
||||
else loadKey(msg.header.target.toString, classOf[PublicKey])
|
||||
val asymmetricCipher = Cipher.getInstance(PublicKeyAlgorithm)
|
||||
asymmetricCipher.init(Cipher.WRAP_MODE, publicKey)
|
||||
lazy val defaultKey = loadKey(msg.header.target.toString, classOf[PublicKey])
|
||||
asymmetricCipher.init(Cipher.WRAP_MODE, key.getOrElse(defaultKey))
|
||||
|
||||
new Message(msg.header,
|
||||
new CryptoData(msg.crypto.signature, Option(asymmetricCipher.wrap(secretKey))), encrypted)
|
||||
new CryptoData(None, Option(asymmetricCipher.wrap(secretKey))), encrypted)
|
||||
}
|
||||
|
||||
def decrypt(msg: Message): Message = {
|
||||
private def decrypt(msg: Message): Message = {
|
||||
// Asymmetric decryption of secret key
|
||||
val asymmetricCipher = Cipher.getInstance(PublicKeyAlgorithm)
|
||||
asymmetricCipher.init(Cipher.UNWRAP_MODE, loadKey(PrivateKeyAlias, classOf[PrivateKey]))
|
||||
|
|
|
@ -32,7 +32,7 @@ class CryptoTest extends TestCase {
|
|||
def testSignVerify(): Unit = {
|
||||
MessageTest.messages.foreach { m =>
|
||||
val signed = crypto.sign(m)
|
||||
assertTrue(crypto.verify(signed, crypto.getLocalPublicKey))
|
||||
assertTrue(crypto.verify(signed, Option(crypto.getLocalPublicKey)))
|
||||
assertEquals(m.header, signed.header)
|
||||
assertEquals(m.body, signed.body)
|
||||
}
|
||||
|
@ -40,9 +40,9 @@ class CryptoTest extends TestCase {
|
|||
|
||||
def testEncryptDecrypt(): Unit = {
|
||||
MessageTest.messages.foreach{ m =>
|
||||
val encrypted = crypto.encrypt(crypto.sign(m), crypto.getLocalPublicKey)
|
||||
val decrypted = crypto.decrypt(encrypted)
|
||||
assertEquals(m.body, decrypted.body)
|
||||
val encrypted = crypto.encryptAndSign(m, Option(crypto.getLocalPublicKey))
|
||||
val decrypted = crypto.verifyAndDecrypt(encrypted, Option(crypto.getLocalPublicKey))
|
||||
assertEquals(m.body, decrypted.get.body)
|
||||
assertEquals(m.header, encrypted.header)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,21 +48,19 @@ class MessageTest extends TestCase {
|
|||
val read = Message.read(new ByteArrayInputStream(bytes))
|
||||
|
||||
assertEquals(signed, read)
|
||||
assertTrue(crypto.verify(read, crypto.getLocalPublicKey))
|
||||
assertTrue(crypto.verify(read, Option(crypto.getLocalPublicKey)))
|
||||
}
|
||||
|
||||
def testSerializeEncrypted(): Unit = {
|
||||
MessageTest.messages.foreach{ m =>
|
||||
val signed = crypto.sign(m)
|
||||
val encrypted = crypto.encrypt(signed, crypto.getLocalPublicKey)
|
||||
val encrypted = crypto.encryptAndSign(m, Option(crypto.getLocalPublicKey))
|
||||
val bytes = encrypted.write
|
||||
|
||||
val read = Message.read(new ByteArrayInputStream(bytes))
|
||||
assertEquals(encrypted.crypto, read.crypto)
|
||||
val decrypted = crypto.decrypt(read)
|
||||
assertEquals(m.header, decrypted.header)
|
||||
assertEquals(m.body, decrypted.body)
|
||||
assertTrue(crypto.verify(decrypted, crypto.getLocalPublicKey))
|
||||
val decrypted = crypto.verifyAndDecrypt(read, Option(crypto.getLocalPublicKey))
|
||||
assertEquals(m.header, decrypted.get.header)
|
||||
assertEquals(m.body, decrypted.get.body)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue