151 строка
3.9 KiB
Go
151 строка
3.9 KiB
Go
// Copyright 2012 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package crypt provides simple, password-based encryption and decryption of data blobs.
|
|
package crypt
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/hmac"
|
|
"crypto/rand"
|
|
"crypto/sha1"
|
|
"fmt"
|
|
"io"
|
|
|
|
"code.google.com/p/go.crypto/pbkdf2"
|
|
)
|
|
|
|
// This program manipulates encrypted, signed packets with the following format:
|
|
// 1 byte version
|
|
// 8 byte salt
|
|
// 4 byte key hash
|
|
// aes.BlockSize-byte IV
|
|
// aes.BlockSize-byte encryption (maybe longer)
|
|
// sha1.Size-byte HMAC signature
|
|
|
|
const version = 0
|
|
|
|
// deriveKey returns the AES key, HMAC-SHA1 key, and key hash for
|
|
// the given password, salt combination.
|
|
func deriveKey(password string, salt []byte) (aesKey, hmacKey, keyHash []byte) {
|
|
const keySize = 16
|
|
key := pbkdf2.Key([]byte(password), salt, 4096, 2*keySize, sha1.New)
|
|
aesKey = key[:keySize]
|
|
hmacKey = key[keySize:]
|
|
h := sha1.New()
|
|
h.Write(key)
|
|
keyHash = h.Sum(nil)[:4]
|
|
return
|
|
}
|
|
|
|
// Encrypt encrypts the plaintext into an encrypted packet
|
|
// using the given password. The password is required for
|
|
// decryption.
|
|
func Encrypt(password string, plaintext []byte) (encrypted []byte, err error) {
|
|
// Derive key material from password and salt.
|
|
salt := make([]byte, 8)
|
|
_, err = io.ReadFull(rand.Reader, salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
aesKey, hmacKey, keyHash := deriveKey(password, salt)
|
|
|
|
// Pad.
|
|
n := aes.BlockSize - len(plaintext)%aes.BlockSize
|
|
dec := make([]byte, len(plaintext)+n)
|
|
copy(dec, plaintext)
|
|
for i := len(plaintext); i < len(dec); i++ {
|
|
dec[i] = byte(n)
|
|
}
|
|
|
|
// Encrypt.
|
|
iv := make([]byte, aes.BlockSize)
|
|
_, err = io.ReadFull(rand.Reader, iv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
aesBlock, err := aes.NewCipher(aesKey)
|
|
if err != nil {
|
|
// Cannot happen - key is right size.
|
|
panic("aes: " + err.Error())
|
|
}
|
|
m := cipher.NewCBCEncrypter(aesBlock, iv)
|
|
enc := make([]byte, len(dec))
|
|
m.CryptBlocks(enc, dec)
|
|
|
|
// Construct packet.
|
|
var pkt []byte
|
|
pkt = append(pkt, version)
|
|
pkt = append(pkt, salt...)
|
|
pkt = append(pkt, keyHash...)
|
|
pkt = append(pkt, iv...)
|
|
pkt = append(pkt, enc...)
|
|
|
|
// Sign.
|
|
h := hmac.New(sha1.New, hmacKey)
|
|
h.Write(pkt)
|
|
pkt = append(pkt, h.Sum(nil)...)
|
|
|
|
return pkt, nil
|
|
}
|
|
|
|
// Decrypt decrypts the encrypted packet using the given password.
|
|
// It returns the decrypted data.
|
|
func Decrypt(password string, encrypted []byte) (plaintext []byte, err error) {
|
|
// Pull apart packet.
|
|
pkt := encrypted
|
|
if len(pkt) < 1+8+4+2*aes.BlockSize+sha1.Size {
|
|
return nil, fmt.Errorf("encrypted packet too short")
|
|
}
|
|
vers, pkt := pkt[:1], pkt[1:]
|
|
salt, pkt := pkt[:8], pkt[8:]
|
|
hash, pkt := pkt[:4], pkt[4:]
|
|
iv, pkt := pkt[:aes.BlockSize], pkt[aes.BlockSize:]
|
|
enc, sig := pkt[:len(pkt)-sha1.Size], pkt[len(pkt)-sha1.Size:]
|
|
|
|
if vers[0] != version || len(enc)%aes.BlockSize != 0 {
|
|
return nil, fmt.Errorf("malformed encrypted packet")
|
|
}
|
|
|
|
// Derive key and check against hash.
|
|
aesKey, hmacKey, keyHash := deriveKey(password, salt)
|
|
if !bytes.Equal(hash, keyHash) {
|
|
return nil, fmt.Errorf("incorrect password - %x vs %x", hash, keyHash)
|
|
}
|
|
|
|
// Verify signature.
|
|
h := hmac.New(sha1.New, hmacKey)
|
|
h.Write(encrypted[:len(encrypted)-len(sig)])
|
|
if !bytes.Equal(sig, h.Sum(nil)) {
|
|
return nil, fmt.Errorf("cannot authenticate encrypted packet")
|
|
}
|
|
|
|
// Decrypt.
|
|
aesBlock, err := aes.NewCipher(aesKey)
|
|
if err != nil {
|
|
// Cannot happen - key is right size.
|
|
panic("aes: " + err.Error())
|
|
}
|
|
m := cipher.NewCBCDecrypter(aesBlock, iv)
|
|
dec := make([]byte, len(enc))
|
|
m.CryptBlocks(dec, enc)
|
|
|
|
// Unpad.
|
|
pad := dec[len(dec)-1]
|
|
if pad <= 0 || pad > aes.BlockSize {
|
|
return nil, fmt.Errorf("malformed packet padding")
|
|
}
|
|
for _, b := range dec[len(dec)-int(pad):] {
|
|
if b != pad {
|
|
return nil, fmt.Errorf("malformed packet padding")
|
|
}
|
|
}
|
|
dec = dec[:len(dec)-int(pad)]
|
|
|
|
// Success!
|
|
return dec, nil
|
|
}
|