github.com/dahs81/otto@v0.2.1-0.20160126165905-6400716cf085/otto/encrypt.go (about) 1 package otto 2 3 import ( 4 "bytes" 5 "crypto/aes" 6 "crypto/cipher" 7 "crypto/rand" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "os" 12 13 "golang.org/x/crypto/scrypt" 14 ) 15 16 const ( 17 cryptPrefixV0 = "v0:" 18 cryptKeySaltLen = 32 19 ) 20 21 // cryptWrite is a helper to encrypt data and then write it to a file. 22 // Encryption is done by using bcrypt as a KDF followed by AES-GCM. 23 func cryptWrite(dst string, password string, plaintext []byte) error { 24 keySalt := make([]byte, cryptKeySaltLen) 25 if _, err := rand.Read(keySalt); err != nil { 26 return err 27 } 28 29 key, err := scrypt.Key([]byte(password), keySalt, 16384, 8, 1, 32) 30 if err != nil { 31 return err 32 } 33 34 aesCipher, err := aes.NewCipher(key) 35 if err != nil { 36 return err 37 } 38 39 gcm, err := cipher.NewGCM(aesCipher) 40 if err != nil { 41 return err 42 } 43 44 // Compute random nonce 45 nonce := make([]byte, gcm.NonceSize()) 46 if _, err := rand.Read(nonce); err != nil { 47 return err 48 } 49 50 // Encrypt and tag with GCM 51 out := gcm.Seal(nil, nonce, plaintext, nil) 52 ciphertext := make([]byte, 0, len("v0:")+len(nonce)+len(out)) 53 ciphertext = append(ciphertext, []byte("v0:")...) 54 ciphertext = append(ciphertext, keySalt...) 55 ciphertext = append(ciphertext, nonce...) 56 ciphertext = append(ciphertext, out...) 57 out = nil 58 59 // Create the file for writing, making sure it is opened as 0600 60 // for a little additional security. 61 f, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) 62 if err != nil { 63 return err 64 } 65 defer f.Close() 66 67 _, err = io.Copy(f, bytes.NewReader(ciphertext)) 68 return err 69 } 70 71 func cryptRead(path string, password string) ([]byte, error) { 72 // Read the contents of the path first 73 ciphertext, err := ioutil.ReadFile(path) 74 if err != nil { 75 return nil, err 76 } 77 78 // Verify that the data looks valid 79 if !bytes.HasPrefix(ciphertext, []byte(cryptPrefixV0)) { 80 return nil, fmt.Errorf("corrupt encrypted data") 81 } 82 83 // Read our key salt 84 ciphertext = ciphertext[len(cryptPrefixV0):] 85 keySalt := ciphertext[:32] 86 ciphertext = ciphertext[32:] 87 88 // Derive the key 89 key, err := scrypt.Key([]byte(password), keySalt, 16384, 8, 1, 32) 90 if err != nil { 91 return nil, err 92 } 93 94 // Setup the cipher 95 aesCipher, err := aes.NewCipher(key) 96 if err != nil { 97 return nil, err 98 } 99 100 // Setup the GCM AEAD 101 gcm, err := cipher.NewGCM(aesCipher) 102 if err != nil { 103 return nil, err 104 } 105 106 // Get the nonce and ciphertext out 107 nonce := ciphertext[:gcm.NonceSize()] 108 ciphertext = ciphertext[gcm.NonceSize():] 109 110 // Decrypt 111 return gcm.Open(nil, nonce, ciphertext, nil) 112 }