new helpers for drive decryption (#73)

* new helpers for drive decryption

* modular helper functions and reciprocals

* removed duplicates helper functions

* added mobile wrapper

* unit tests for new helpers

Co-authored-by: wussler <aron@wussler.it>
This commit is contained in:
marinthiercelin 2020-08-27 17:34:46 +02:00 committed by GitHub
parent af371097e0
commit 39c2fa863e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 231 additions and 38 deletions

View file

@ -168,49 +168,18 @@ func DecryptVerifyAttachment(
passphrase, keyPacket, dataPacket []byte, passphrase, keyPacket, dataPacket []byte,
armoredSignature string, armoredSignature string,
) (plainData []byte, err error) { ) (plainData []byte, err error) {
var publicKeyObj, privateKeyObj, unlockedKeyObj *crypto.Key // We decrypt the attachment
var publicKeyRing, privateKeyRing *crypto.KeyRing message, err := decryptAttachment(privateKey, passphrase, keyPacket, dataPacket)
var detachedSignature *crypto.PGPSignature if err != nil {
var message *crypto.PlainMessage
var packets = crypto.NewPGPSplitMessage(keyPacket, dataPacket)
if publicKeyObj, err = crypto.NewKeyFromArmored(publicKey); err != nil {
return nil, err
}
if publicKeyObj.IsPrivate() {
publicKeyObj, err = publicKeyObj.ToPublic()
if err != nil {
return nil, err
}
}
if publicKeyRing, err = crypto.NewKeyRing(publicKeyObj); err != nil {
return nil, err return nil, err
} }
if privateKeyObj, err = crypto.NewKeyFromArmored(privateKey); err != nil { // We verify the signature
var check bool
if check, err = verifyDetachedArmored(publicKey, message, armoredSignature); err != nil {
return nil, err return nil, err
} }
if !check {
if unlockedKeyObj, err = privateKeyObj.Unlock(passphrase); err != nil {
return nil, err
}
defer unlockedKeyObj.ClearPrivateParams()
if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil {
return nil, err
}
if detachedSignature, err = crypto.NewPGPSignatureFromArmored(armoredSignature); err != nil {
return nil, err
}
if message, err = privateKeyRing.DecryptAttachment(packets); err != nil {
return nil, err
}
if publicKeyRing.VerifyDetached(message, detachedSignature, crypto.GetUnixTime()) != nil {
return nil, errors.New("gopenpgp: unable to verify attachment") return nil, errors.New("gopenpgp: unable to verify attachment")
} }
@ -235,6 +204,58 @@ func DecryptBinaryMessageArmored(privateKey string, passphrase []byte, ciphertex
return message.GetBinary(), nil return message.GetBinary(), nil
} }
// EncryptSignArmoredDetached takes a public key for encryption,
// a private key and its passphrase for signature, and the plaintext data
// Returns an armored ciphertext and a detached armored signature.
func EncryptSignArmoredDetached(
publicKey, privateKey string,
passphrase, plainData []byte,
) (ciphertext, signature string, err error) {
var message *crypto.PlainMessage = crypto.NewPlainMessage(plainData)
// We encrypt the message
if ciphertext, err = encryptMessageArmored(publicKey, message); err != nil {
return "", "", err
}
// We sign the message
if signature, err = signDetachedArmored(privateKey, passphrase, message); err != nil {
return "", "", err
}
return ciphertext, signature, nil
}
// DecryptVerifyArmoredDetached decrypts an armored pgp message
// and verify a detached armored signature
// given a publicKey, and a privateKey with its passphrase.
// Returns the plain data or an error on
// signature verification failure.
func DecryptVerifyArmoredDetached(
publicKey, privateKey string,
passphrase []byte,
ciphertext string,
armoredSignature string,
) (plainData []byte, err error) {
var message *crypto.PlainMessage
// We decrypt the message
if message, err = decryptMessageArmored(privateKey, passphrase, ciphertext); err != nil {
return nil, err
}
// We verify the signature
var check bool
if check, err = verifyDetachedArmored(publicKey, message, armoredSignature); err != nil {
return nil, err
}
if !check {
return nil, errors.New("gopenpgp: unable to verify message")
}
return message.GetBinary(), nil
}
func encryptMessageArmored(key string, message *crypto.PlainMessage) (string, error) { func encryptMessageArmored(key string, message *crypto.PlainMessage) (string, error) {
publicKey, err := crypto.NewKeyFromArmored(key) publicKey, err := crypto.NewKeyFromArmored(key)
if publicKey.IsPrivate() { if publicKey.IsPrivate() {
@ -304,3 +325,96 @@ func decryptMessageArmored(privateKey string, passphrase []byte, ciphertext stri
return message, nil return message, nil
} }
func signDetachedArmored(privateKey string, passphrase []byte, message *crypto.PlainMessage) (signature string, err error) {
privateKeyObj, err := crypto.NewKeyFromArmored(privateKey)
if err != nil {
return "", err
}
privateKeyUnlocked, err := privateKeyObj.Unlock(passphrase)
if err != nil {
return "", err
}
defer privateKeyUnlocked.ClearPrivateParams()
privateKeyRing, err := crypto.NewKeyRing(privateKeyUnlocked)
if err != nil {
return "", err
}
detachedSignature, err := privateKeyRing.SignDetached(message)
if err != nil {
return "", err
}
armoredSignature, err := detachedSignature.GetArmored()
if err != nil {
return "", err
}
return armoredSignature, nil
}
func verifyDetachedArmored(publicKey string, message *crypto.PlainMessage, armoredSignature string) (check bool, err error) {
var publicKeyObj *crypto.Key
var publicKeyRing *crypto.KeyRing
var detachedSignature *crypto.PGPSignature
// We prepare the public key for signature verification
if publicKeyObj, err = crypto.NewKeyFromArmored(publicKey); err != nil {
return false, err
}
if publicKeyObj.IsPrivate() {
publicKeyObj, err = publicKeyObj.ToPublic()
if err != nil {
return false, err
}
}
if publicKeyRing, err = crypto.NewKeyRing(publicKeyObj); err != nil {
return false, err
}
// We verify the signature
if detachedSignature, err = crypto.NewPGPSignatureFromArmored(armoredSignature); err != nil {
return false, err
}
if publicKeyRing.VerifyDetached(message, detachedSignature, crypto.GetUnixTime()) != nil {
return false, nil
}
return true, nil
}
func decryptAttachment(
privateKey string,
passphrase, keyPacket, dataPacket []byte,
) (message *crypto.PlainMessage, err error) {
var privateKeyObj, unlockedKeyObj *crypto.Key
var privateKeyRing *crypto.KeyRing
packets := crypto.NewPGPSplitMessage(keyPacket, dataPacket)
// prepare the private key for decryption
if privateKeyObj, err = crypto.NewKeyFromArmored(privateKey); err != nil {
return nil, err
}
if unlockedKeyObj, err = privateKeyObj.Unlock(passphrase); err != nil {
return nil, err
}
defer unlockedKeyObj.ClearPrivateParams()
if privateKeyRing, err = crypto.NewKeyRing(unlockedKeyObj); err != nil {
return nil, err
}
if message, err = privateKeyRing.DecryptAttachment(packets); err != nil {
return nil, err
}
return message, nil
}

View file

@ -1,6 +1,7 @@
package helper package helper
import ( import (
"bytes"
"testing" "testing"
"github.com/ProtonMail/gopenpgp/v2/crypto" "github.com/ProtonMail/gopenpgp/v2/crypto"
@ -154,3 +155,64 @@ func TestArmoredBinaryMessageEncryption(t *testing.T) {
assert.Exactly(t, plainData, decrypted) assert.Exactly(t, plainData, decrypted)
} }
func TestEncryptSignArmoredDetached(t *testing.T) {
plainData := []byte("Secret message")
privateKeyString := readTestFile("keyring_privateKey", false)
privateKey, err := crypto.NewKeyFromArmored(privateKeyString)
if err != nil {
t.Fatal("Error reading the test private key: ", err)
}
publicKeyString, err := privateKey.GetArmoredPublicKey()
if err != nil {
t.Fatal("Error reading the test public key: ", err)
}
armoredCiphertext, armoredSignature, err := EncryptSignArmoredDetached(
publicKeyString,
privateKeyString,
testMailboxPassword, // Password defined in base_test
plainData,
)
if err != nil {
t.Fatal("Expected no error while encrypting and signing, got:", err)
}
decrypted, err := DecryptVerifyArmoredDetached(
publicKeyString,
privateKeyString,
testMailboxPassword,
armoredCiphertext,
armoredSignature,
)
if err != nil {
t.Fatal("Expected no error while decrypting and verifying, got:", err)
}
if !bytes.Equal(decrypted, plainData) {
t.Error("Decrypted is not equal to the plaintext")
}
_, modifiedSignature, err := EncryptSignArmoredDetached(
publicKeyString,
privateKeyString,
testMailboxPassword, // Password defined in base_test
[]byte("Different message"),
)
if err != nil {
t.Fatal("Expected no error while encrypting and signing, got:", err)
}
_, err = DecryptVerifyArmoredDetached(
publicKeyString,
privateKeyString,
testMailboxPassword,
armoredCiphertext,
modifiedSignature,
)
if err == nil {
t.Fatal("Expected an error while decrypting and verifying with a wrong signature")
}
}

View file

@ -79,3 +79,20 @@ func GetJsonSHA256Fingerprints(publicKey string) ([]byte, error) {
return json.Marshal(key.GetSHA256Fingerprints()) return json.Marshal(key.GetSHA256Fingerprints())
} }
type EncryptSignArmoredDetachedMobileResult struct {
Ciphertext, Signature string
}
//EncryptSignArmoredDetachedMobile wraps the EncryptSignArmoredDetached method
//to have only one return argument for mobile.
func EncryptSignArmoredDetachedMobile(
publicKey, privateKey string,
passphrase, plainData []byte,
) (wrappedTuple *EncryptSignArmoredDetachedMobileResult, err error) {
ciphertext, signature, err := EncryptSignArmoredDetached(publicKey, privateKey, passphrase, plainData)
if err != nil {
return nil, err
}
return &EncryptSignArmoredDetachedMobileResult{ciphertext, signature}, nil
}