Skip to content
Permalink
 
 
Cannot retrieve contributors at this time
130 lines (114 sloc) 4.78 KB
/*
Copyright 2016, Cossack Labs Limited
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package base
import (
"bytes"
"encoding/binary"
"errors"
"github.com/cossacklabs/acra/keystore"
"github.com/cossacklabs/acra/utils"
"github.com/cossacklabs/themis/gothemis/cell"
"github.com/cossacklabs/themis/gothemis/keys"
"github.com/cossacklabs/themis/gothemis/message"
)
// GetDataLengthFromAcraStruct unpack data length value from AcraStruct
func GetDataLengthFromAcraStruct(data []byte) int {
dataLengthBlock := data[GetMinAcraStructLength()-DataLengthSize : GetMinAcraStructLength()]
return int(binary.LittleEndian.Uint64(dataLengthBlock))
}
// GetMinAcraStructLength returns minimal length of AcraStruct
// because in golang we can't declare byte array as constant we need to calculate length of TagBegin in runtime
// or hardcode as constant and maintain len(TagBegin) == CONST_VALUE
func GetMinAcraStructLength() int {
return len(TagBegin) + KeyBlockLength + DataLengthSize
}
// Errors show incorrect AcraStruct length
var (
ErrIncorrectAcraStructTagBegin = errors.New("AcraStruct has incorrect TagBegin")
ErrIncorrectAcraStructLength = errors.New("AcraStruct has incorrect length")
ErrIncorrectAcraStructDataLength = errors.New("AcraStruct has incorrect data length value")
)
// ErrNoPrivateKeys is returned when DecryptRotatedAcrastruct is given an empty key list
var ErrNoPrivateKeys = errors.New("cannot decrypt AcraStruct with empty key list")
// ValidateAcraStructLength check that data has minimal length for AcraStruct and data block equal to data length in AcraStruct
func ValidateAcraStructLength(data []byte) error {
baseLength := GetMinAcraStructLength()
if len(data) < baseLength {
return ErrIncorrectAcraStructLength
}
if !bytes.Equal(data[:len(TagBegin)], TagBegin) {
return ErrIncorrectAcraStructTagBegin
}
dataLength := GetDataLengthFromAcraStruct(data)
if dataLength != len(data[GetMinAcraStructLength():]) {
return ErrIncorrectAcraStructDataLength
}
return nil
}
// DecryptAcrastruct returns plaintext data from AcraStruct, decrypting it using Themis SecureCell in Seal mode,
// using zone as context and privateKey as decryption key.
// Returns error if decryption failed.
func DecryptAcrastruct(data []byte, privateKey *keys.PrivateKey, zone []byte) ([]byte, error) {
if err := ValidateAcraStructLength(data); err != nil {
return nil, err
}
innerData := data[len(TagBegin):]
pubkey := &keys.PublicKey{Value: innerData[:PublicKeyLength]}
smessage := message.New(privateKey, pubkey)
symmetricKey, err := smessage.Unwrap(innerData[PublicKeyLength:KeyBlockLength])
if err != nil {
return []byte{}, err
}
//
var length uint64
// convert from little endian
err = binary.Read(bytes.NewReader(innerData[KeyBlockLength:KeyBlockLength+DataLengthSize]), binary.LittleEndian, &length)
if err != nil {
return []byte{}, err
}
scell := cell.New(symmetricKey, cell.ModeSeal)
decrypted, err := scell.Unprotect(innerData[KeyBlockLength+DataLengthSize:], nil, zone)
// fill zero symmetric_key
utils.ZeroizeSymmetricKey(symmetricKey)
if err != nil {
return []byte{}, err
}
return decrypted, nil
}
// DecryptRotatedAcrastruct tries decrypting an AcraStruct with a set of rotated keys.
// It either returns decrypted data if one of the keys succeeds, or an error if none is good.
func DecryptRotatedAcrastruct(data []byte, privateKeys []*keys.PrivateKey, zone []byte) ([]byte, error) {
var err error = ErrNoPrivateKeys
var decryptedData []byte
for _, privateKey := range privateKeys {
decryptedData, err = DecryptAcrastruct(data, privateKey, zone)
if err == nil {
return decryptedData, nil
}
}
return nil, err
}
// CheckPoisonRecord checks if AcraStruct could be decrypted using Poison Record private key.
// Returns true if AcraStruct is poison record, returns false otherwise.
// Returns error if Poison record key is not found.
func CheckPoisonRecord(data []byte, keystorage keystore.PoisonKeyStore) (bool, error) {
// If we fail to get poison record keys, propagate the error assuming it is a poison record.
poisonKeys, err := keystorage.GetPoisonPrivateKeys()
if err != nil {
return true, err
}
defer utils.ZeroizePrivateKeys(poisonKeys)
// Try decrypting the data. It is a poison record if it can be decrypted without an error.
_, err = DecryptRotatedAcrastruct(data, poisonKeys, nil)
return err == nil, nil
}