gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/skyfileencryption.go (about) 1 package renter 2 3 // skyfile_encryption.go provides utilities for encrypting and decrypting 4 // skyfiles. 5 6 import ( 7 "gitlab.com/NebulousLabs/errors" 8 9 "gitlab.com/SkynetLabs/skyd/build" 10 "gitlab.com/SkynetLabs/skyd/skykey" 11 "gitlab.com/SkynetLabs/skyd/skymodules" 12 "go.sia.tech/siad/modules" 13 14 "github.com/aead/chacha20/chacha" 15 ) 16 17 var errNoSkykeyMatchesSkyfileEncryptionID = errors.New("Unable to find matching skykey for public ID encryption") 18 19 // DecryptBaseSector attempts to decrypt the baseSector. If it has the 20 // necessary Skykey, it will decrypt the baseSector in-place. It returns the 21 // file-specific skykey to be used for decrypting the rest of the associated 22 // skyfile. 23 func (r *Renter) DecryptBaseSector(baseSector []byte) (skykey.Skykey, error) { 24 if err := r.tg.Add(); err != nil { 25 return skykey.Skykey{}, err 26 } 27 defer r.tg.Done() 28 return r.managedDecryptBaseSector(baseSector) 29 } 30 31 // managedCheckSkyfileEncryptionIDMatch tries to find a Skykey that can decrypt 32 // the identifier and be used for decrypting the associated skyfile. It returns 33 // an error if it is not found. 34 func (r *Renter) managedCheckSkyfileEncryptionIDMatch(encryptionIdentifier []byte, nonce []byte) (skykey.Skykey, error) { 35 allSkykeys := r.staticSkykeyManager.Skykeys() 36 for _, sk := range allSkykeys { 37 matches, err := sk.MatchesSkyfileEncryptionID(encryptionIdentifier, nonce) 38 if err != nil { 39 r.staticLog.Debugln("SkykeyEncryptionID match err", err) 40 continue 41 } 42 if matches { 43 return sk, nil 44 } 45 } 46 return skykey.Skykey{}, errNoSkykeyMatchesSkyfileEncryptionID 47 } 48 49 // managedDecryptBaseSector attempts to decrypt the baseSector. If it has the 50 // necessary Skykey, it will decrypt the baseSector in-place. It returns the 51 // file-specific skykey to be used for decrypting the rest of the associated 52 // skyfile. 53 func (r *Renter) managedDecryptBaseSector(baseSector []byte) (skykey.Skykey, error) { 54 // Sanity check - baseSector should not be more than modules.SectorSize. 55 // Note that the base sector may be smaller in the event of a packed 56 // skyfile. 57 if uint64(len(baseSector)) > modules.SectorSize { 58 build.Critical("decryptBaseSector given a baseSector that is too large") 59 return skykey.Skykey{}, errors.New("baseSector too large") 60 } 61 var sl skymodules.SkyfileLayout 62 sl.Decode(baseSector) 63 64 if !skymodules.IsEncryptedLayout(sl) { 65 build.Critical("Expected layout to be marked as encrypted!") 66 } 67 68 // Get the nonce to be used for getting private-id skykeys, and for deriving the 69 // file-specific skykey. 70 nonce := make([]byte, chacha.XNonceSize) 71 copy(nonce[:], sl.KeyData[skykey.SkykeyIDLen:skykey.SkykeyIDLen+chacha.XNonceSize]) 72 73 // Grab the key ID from the layout. 74 var keyID skykey.SkykeyID 75 copy(keyID[:], sl.KeyData[:skykey.SkykeyIDLen]) 76 77 // Try to get the skykey associated with that ID. 78 masterSkykey, err := r.staticSkykeyManager.KeyByID(keyID) 79 // If the ID is unknown, use the key ID as an encryption identifier and try 80 // finding the associated skykey. 81 if errors.Contains(err, skykey.ErrNoSkykeysWithThatID) { 82 masterSkykey, err = r.managedCheckSkyfileEncryptionIDMatch(keyID[:], nonce) 83 } 84 if err != nil { 85 return skykey.Skykey{}, errors.AddContext(err, "Unable to find associated skykey") 86 } 87 88 // Derive the file-specific key. 89 fileSkykey, err := masterSkykey.SubkeyWithNonce(nonce) 90 if err != nil { 91 return skykey.Skykey{}, errors.AddContext(err, "Unable to derive file-specific subkey") 92 } 93 94 // Derive the base sector subkey and use it to decrypt the base sector. 95 baseSectorKey, err := fileSkykey.DeriveSubkey(skymodules.BaseSectorNonceDerivation[:]) 96 if err != nil { 97 return skykey.Skykey{}, errors.AddContext(err, "Unable to derive baseSector subkey") 98 } 99 100 // Get the cipherkey. 101 ck, err := baseSectorKey.CipherKey() 102 if err != nil { 103 return skykey.Skykey{}, errors.AddContext(err, "Unable to get baseSector cipherkey") 104 } 105 106 _, err = ck.DecryptBytesInPlace(baseSector, 0) 107 if err != nil { 108 return skykey.Skykey{}, errors.New("Error decrypting baseSector for download") 109 } 110 111 // Save the visible-by-default fields of the baseSector's layout. 112 version := sl.Version 113 cipherType := sl.CipherType 114 var keyData [64]byte 115 copy(keyData[:], sl.KeyData[:]) 116 117 // Decode the now decrypted layout. 118 sl.Decode(baseSector) 119 120 // Reset the visible-by-default fields. 121 // (They were turned into random values by the decryption) 122 sl.Version = version 123 sl.CipherType = cipherType 124 copy(sl.KeyData[:], keyData[:]) 125 126 // Now re-copy the decrypted layout into the decrypted baseSector. 127 copy(baseSector[:skymodules.SkyfileLayoutSize], sl.Encode()) 128 129 return fileSkykey, nil 130 } 131 132 // encryptBaseSectorWithSkykey encrypts the baseSector in place using the given 133 // Skykey. Certain fields of the layout are restored in plaintext into the 134 // encrypted baseSector to indicate to downloaders what Skykey was used. 135 func encryptBaseSectorWithSkykey(baseSector []byte, plaintextLayout skymodules.SkyfileLayout, sk skykey.Skykey) error { 136 baseSectorKey, err := sk.DeriveSubkey(skymodules.BaseSectorNonceDerivation[:]) 137 if err != nil { 138 return errors.AddContext(err, "Unable to derive baseSector subkey") 139 } 140 141 // Get the cipherkey. 142 ck, err := baseSectorKey.CipherKey() 143 if err != nil { 144 return errors.AddContext(err, "Unable to get baseSector cipherkey") 145 } 146 147 // Encrypt the base sector. This is calling DecryptBytesInPlace because 148 // we only allow the stream cipher ChaCha for which encryption and 149 // decryption are the same thing since its just xoring the data with a 150 // keystream. 151 _, err = ck.DecryptBytesInPlace(baseSector, 0) 152 if err != nil { 153 return errors.New("Error decrypting baseSector for download") 154 } 155 156 // Re-add the visible-by-default fields of the baseSector. 157 var encryptedLayout skymodules.SkyfileLayout 158 encryptedLayout.Decode(baseSector) 159 encryptedLayout.Version = plaintextLayout.Version 160 encryptedLayout.CipherType = baseSectorKey.CipherType() 161 162 // Add the key ID or the encrypted skyfile identifier, depending on the key 163 // type. 164 switch sk.Type { 165 case skykey.TypePublicID: 166 keyID := sk.ID() 167 copy(encryptedLayout.KeyData[:skykey.SkykeyIDLen], keyID[:]) 168 169 case skykey.TypePrivateID: 170 encryptedIdentifier, err := sk.GenerateSkyfileEncryptionID() 171 if err != nil { 172 return errors.AddContext(err, "Unable to generate encrypted skyfile ID") 173 } 174 copy(encryptedLayout.KeyData[:skykey.SkykeyIDLen], encryptedIdentifier[:]) 175 176 default: 177 build.Critical("No encryption implemented for this skykey type") 178 return errors.AddContext(errors.New("No encryption implemented for skykey type"), string(sk.Type)) 179 } 180 181 // Add the nonce to the base sector, in plaintext. 182 nonce := sk.Nonce() 183 copy(encryptedLayout.KeyData[skykey.SkykeyIDLen:skykey.SkykeyIDLen+len(nonce)], nonce[:]) 184 185 // Now re-copy the encrypted layout into the baseSector. 186 copy(baseSector[:skymodules.SkyfileLayoutSize], encryptedLayout.Encode()) 187 return nil 188 } 189 190 // encryptionEnabled checks if encryption is enabled for the 191 // SkyfileUploadParameters. It returns true if either the SkykeyName or SkykeyID 192 // is set 193 func encryptionEnabled(sup *skymodules.SkyfileUploadParameters) bool { 194 return sup.SkykeyName != "" || sup.SkykeyID != skykey.SkykeyID{} 195 } 196 197 // generateCipherKey generates a Cipher Key for the FileUploadParams from the 198 // SkyfileUploadParameters 199 func generateCipherKey(fup *skymodules.FileUploadParams, sup skymodules.SkyfileUploadParameters) error { 200 if encryptionEnabled(&sup) { 201 fanoutSkykey, err := sup.FileSpecificSkykey.DeriveSubkey(skymodules.FanoutNonceDerivation[:]) 202 if err != nil { 203 return errors.AddContext(err, "unable to derive fanout subkey") 204 } 205 fup.CipherKey, err = fanoutSkykey.CipherKey() 206 if err != nil { 207 return errors.AddContext(err, "unable to get skykey cipherkey") 208 } 209 fup.CipherType = sup.FileSpecificSkykey.CipherType() 210 } 211 return nil 212 } 213 214 // managedGenerateFilekey generates the FileSpecificSkykey to be used for 215 // encryption and sets it in the SkyfileUploadParameters 216 func (r *Renter) managedGenerateFilekey(sup *skymodules.SkyfileUploadParameters, nonce []byte) error { 217 // If encryption is not enabled then nothing to do. 218 if !encryptionEnabled(sup) { 219 return nil 220 } 221 222 // Get the Key 223 var key skykey.Skykey 224 var err error 225 if sup.SkykeyName != "" { 226 key, err = r.SkykeyByName(sup.SkykeyName) 227 } else { 228 key, err = r.SkykeyByID(sup.SkykeyID) 229 } 230 if err != nil { 231 return errors.AddContext(err, "unable to get skykey") 232 } 233 234 // Generate the Subkey 235 if len(nonce) == 0 { 236 sup.FileSpecificSkykey, err = key.GenerateFileSpecificSubkey() 237 } else { 238 sup.FileSpecificSkykey, err = key.SubkeyWithNonce(nonce) 239 } 240 if err != nil { 241 return errors.AddContext(err, "unable to generate subkey") 242 } 243 return nil 244 }