// ExtractDecryptInfo iterates through the header using recipientKey and // attempts to decrypt any DecryptInfoEntry using the provided ephemeral key. // If unsuccessful after iterating through all decryptInfo objects, returns ErrCannotDecrypt. func (hdr *miniLockv1Header) ExtractDecryptInfo(recipientKey *taber.Keys) (nonce []byte, DI *DecryptInfoEntry, err error) { var ( ephemKey *taber.Keys encDI []byte nonceS string ) ephemKey = new(taber.Keys) ephemKey.Public = hdr.Ephemeral if err != nil { return nil, nil, err } // Look for a DI we can decrypt with recipientKey // TODO: Make this concurrent! for nonceS, encDI = range hdr.DecryptInfo { nonce, err := base64.StdEncoding.DecodeString(nonceS) if err != nil { return nil, nil, err } DI, err = DecryptDecryptInfo(encDI, nonce, ephemKey, recipientKey) if err == ErrCannotDecrypt { continue } else if err != nil { return nil, nil, err } recipID, err := recipientKey.EncodeID() if err != nil { return nil, nil, err } if DI.RecipientID != recipID { return nil, nil, ErrBadRecipient } return nonce, DI, nil } return nil, nil, ErrCannotDecrypt }
// This is an entry point that largely defines "normal" miniLock behaviour. // If sendToSender is true, then the sender's ID is added to recipients. func EncryptFileContentsWithStrings(filename string, fileContents []byte, senderEmail, senderPassphrase string, sendToSender bool, recipientIDs ...string) (miniLockContents []byte, err error) { var ( senderKey, this_recipient *taber.Keys recipientKeyList []*taber.Keys this_id string ) senderKey, err = taber.FromEmailAndPassphrase(senderEmail, senderPassphrase) if err != nil { return nil, err } if sendToSender { this_id, err = senderKey.EncodeID() if err != nil { return nil, err } recipientIDs = append(recipientIDs, this_id) } recipientKeyList = make([]*taber.Keys, 0, len(recipientIDs)) // TODO: Randomise iteration here? for _, this_id = range recipientIDs { this_recipient, err = taber.FromID(this_id) if err != nil { return nil, err } recipientKeyList = append(recipientKeyList, this_recipient) } miniLockContents, err = EncryptFileContents(filename, fileContents, senderKey, recipientKeyList...) if err != nil { return nil, err } return miniLockContents, nil }
// DecryptFileContentsWithStrings is the highest-level API for decryption. // It uses the recipient's email and passphrase to generate their key, attempts // decryption, and wipes keys when finished. func DecryptFileContentsWithStrings(fileContents []byte, recipientEmail, recipientPassphrase string) (senderID, filename string, contents []byte, err error) { var recipientKey *taber.Keys recipientKey, err = taber.FromEmailAndPassphrase(recipientEmail, recipientPassphrase) if err != nil { return } defer recipientKey.Wipe() return DecryptFileContents(fileContents, recipientKey) }
// Encrypt a decryptInfo struct using the ephemeral pubkey and the same nonce as the enclosed fileInfo. func EncryptDecryptInfo(di *DecryptInfoEntry, nonce []byte, ephemKey, recipientKey *taber.Keys) ([]byte, error) { plain, err := json.Marshal(di) if err != nil { return nil, err } // NaClKeypair.Encrypt(plaintext, nonce []byte, to *NaClKeypair) (ciphertext []byte, err error) di_enc, err := ephemKey.Encrypt(plain, nonce, recipientKey) if err != nil { return nil, err } return di_enc, nil }
// DecryptDecryptInfo is used to extract a decryptInfo object by attempting decryption // with a given recipientKey. This must be attempted for each decryptInfo in the header // until one works or none work, as miniLock deliberately provides no indication of // intended recipients. func DecryptDecryptInfo(diEnc, nonce []byte, ephemPubkey, recipientKey *taber.Keys) (*DecryptInfoEntry, error) { plain, err := recipientKey.Decrypt(diEnc, nonce, ephemPubkey) if err != nil { return nil, ErrCannotDecrypt } di := new(DecryptInfoEntry) err = json.Unmarshal(plain, di) if err != nil { return nil, err } return di, nil }
// ExtractFileInfo pulls out the fileInfo object from the decryptInfo object, // authenticating encryption from the sender. func (di *DecryptInfoEntry) ExtractFileInfo(nonce []byte, recipientKey *taber.Keys) (*FileInfo, error) { // Return on failure: minilockutils.DecryptionError senderPubkey, err := di.SenderPubkey() if err != nil { return nil, err } plain, err := recipientKey.Decrypt(di.FileInfoEnc, nonce, senderPubkey) if err != nil { return nil, ErrCannotDecrypt } fi := new(FileInfo) err = json.Unmarshal(plain, fi) if err != nil { return nil, err } return fi, nil }
func NewDecryptInfoEntry(nonce []byte, fileinfo *FileInfo, senderKey, recipientKey *taber.Keys) (*DecryptInfoEntry, error) { encoded_fi, err := json.Marshal(fileinfo) if err != nil { return nil, err } cipher_fi, err := senderKey.Encrypt(encoded_fi, nonce, recipientKey) if err != nil { return nil, err } senderID, err := senderKey.EncodeID() if err != nil { return nil, err } recipientID, err := recipientKey.EncodeID() if err != nil { return nil, err } return &DecryptInfoEntry{SenderID: senderID, RecipientID: recipientID, FileInfoEnc: cipher_fi}, nil }