Files
mostlymatter/vendor/github.com/mattermost/rsc/crypt/crypt.go
Christopher Speller 38ee83e45b Moving to glide
2016-05-12 23:56:07 -04:00

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
}