Skip to content

Commit b926a0b

Browse files
committed
Add crypto-refresh (RFC 9580) content key packet for file uploads
Proton Drive's new file-content format uses a v6 PKESK content key packet and a v2 SEIPD (AES-256-GCM) data packet. Generate the content session key with an RFC 9580 + AES-GCM handle (protonDrivePGP) so it carries the v6 flag, and encrypt the content key packet to the (v6) file node key with the same handle, producing a v6 PKESK. Name, passphrase and xattr encryption are left unchanged (they stay non-AEAD).
1 parent 134e979 commit b926a0b

2 files changed

Lines changed: 31 additions & 2 deletions

File tree

crypto_v3_compat.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,36 @@ import (
1414
"sync/atomic"
1515
"time"
1616

17+
"github.com/ProtonMail/go-crypto/openpgp/packet"
1718
"github.com/ProtonMail/gopenpgp/v3/armor"
1819
"github.com/ProtonMail/gopenpgp/v3/crypto"
20+
"github.com/ProtonMail/gopenpgp/v3/profile"
1921
)
2022

23+
// protonDrivePGP returns a gopenpgp handle configured for Proton Drive's
24+
// crypto-refresh (RFC 9580) file-content format: a v6 key packet (PKESK) and a
25+
// v2 SEIPD data packet using AES-256-GCM. The RFC9580 profile defaults the AEAD
26+
// mode to OCB, so we override it to GCM (the mode Proton Drive requires).
27+
//
28+
// A session key generated from this handle carries the v6 flag, which selects
29+
// the v6 PKESK when the session key is encrypted to the (v6) file node key, and
30+
// the v2 SEIPD when it later encrypts the file blocks.
31+
func protonDrivePGP() *crypto.PGPHandle {
32+
p := profile.RFC9580()
33+
p.AeadEncryption = &packet.AEADConfig{DefaultMode: packet.AEADModeGCM}
34+
return crypto.PGPWithProfile(p)
35+
}
36+
37+
// encryptContentKeyPacket wraps a Proton Drive content session key into a v6
38+
// PKESK key packet for the given recipient (node) keyring.
39+
func encryptContentKeyPacket(kr *crypto.KeyRing, sk *crypto.SessionKey) ([]byte, error) {
40+
eh, err := protonDrivePGP().Encryption().Recipients(kr).New()
41+
if err != nil {
42+
return nil, err
43+
}
44+
return eh.EncryptSessionKey(sk)
45+
}
46+
2147
// serverTimeUnix stores the most recently observed server Unix time, sampled
2248
// from the Date response header. v2 used a global crypto.UpdateTime for this
2349
// purpose; v3 has no global clock, so we track it ourselves and pass it into

link_file_types.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,15 @@ func (createFileReq *CreateFileReq) SetHash(name string, hashKey []byte) error {
112112
}
113113

114114
func (createFileReq *CreateFileReq) SetContentKeyPacketAndSignature(kr *crypto.KeyRing) (*crypto.SessionKey, error) {
115-
newSessionKey, err := crypto.PGP().GenerateSessionKey()
115+
// Generate the content session key with the crypto-refresh handle so it
116+
// carries the v6 flag; this is what makes the content key packet a v6
117+
// PKESK and the file data blocks v2 SEIPD (see protonDrivePGP).
118+
newSessionKey, err := protonDrivePGP().GenerateSessionKey()
116119
if err != nil {
117120
return nil, err
118121
}
119122

120-
encSessionKey, err := encryptSessionKey(kr, newSessionKey)
123+
encSessionKey, err := encryptContentKeyPacket(kr, newSessionKey)
121124
if err != nil {
122125
return nil, err
123126
}

0 commit comments

Comments
 (0)