mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-01-08 07:11:48 +00:00
193 lines
4.3 KiB
Go
193 lines
4.3 KiB
Go
// Package keycache provides the ability to hold active keys in memory
|
|
// for the Red October server.
|
|
//
|
|
// Copyright (c) 2013 CloudFlare, Inc.
|
|
|
|
package keycache
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha1"
|
|
"errors"
|
|
"github.com/cloudflare/redoctober/passvault"
|
|
"log"
|
|
"time"
|
|
)
|
|
|
|
// UserKeys is the set of decrypted keys in memory, indexed by name.
|
|
var UserKeys map[string]ActiveUser = make(map[string]ActiveUser)
|
|
|
|
// ActiveUser holds the information about an actively delegated key.
|
|
type ActiveUser struct {
|
|
Admin bool
|
|
Type string
|
|
Expiry time.Time
|
|
Uses int
|
|
|
|
aesKey []byte
|
|
rsaKey rsa.PrivateKey
|
|
}
|
|
|
|
// matchUser returns the matching active user if present
|
|
// and a boolean to indicate its presence.
|
|
func matchUser(name string) (out ActiveUser, present bool) {
|
|
out, present = UserKeys[name]
|
|
return
|
|
}
|
|
|
|
// setUser takes an ActiveUser and adds it to the cache.
|
|
func setUser(in ActiveUser, name string) {
|
|
UserKeys[name] = in
|
|
}
|
|
|
|
// useKey decrements the counter on an active key
|
|
// for decryption or symmetric encryption
|
|
func useKey(name string) {
|
|
if val, present := matchUser(name); present {
|
|
val.Uses -= 1
|
|
setUser(val, name)
|
|
}
|
|
}
|
|
|
|
// GetSummary returns the list of active user keys.
|
|
func GetSummary() map[string]ActiveUser {
|
|
return UserKeys
|
|
}
|
|
|
|
// FlushCache removes all delegated keys.
|
|
func FlushCache() {
|
|
for name := range UserKeys {
|
|
delete(UserKeys, name)
|
|
}
|
|
}
|
|
|
|
// Refresh purges all expired or used up keys.
|
|
func Refresh() {
|
|
for name, active := range UserKeys {
|
|
if active.Expiry.Before(time.Now()) || active.Uses <= 0 {
|
|
log.Println("Record expired", name, active.Expiry)
|
|
delete(UserKeys, name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// AddKeyFromRecord decrypts a key for a given record and adds it to the cache.
|
|
func AddKeyFromRecord(record passvault.PasswordRecord, name string, password string, uses int, durationString string) (err error) {
|
|
var current ActiveUser
|
|
|
|
Refresh()
|
|
|
|
// compute exipiration
|
|
duration, err := time.ParseDuration(durationString)
|
|
if err != nil {
|
|
return
|
|
}
|
|
current.Uses = uses
|
|
current.Expiry = time.Now().Add(duration)
|
|
|
|
// get decryption keys
|
|
switch record.Type {
|
|
case passvault.AESRecord:
|
|
current.aesKey, err = record.GetKeyAES(password)
|
|
case passvault.RSARecord:
|
|
current.rsaKey, err = record.GetKeyRSA(password)
|
|
default:
|
|
err = errors.New("Unknown record type")
|
|
}
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// set types
|
|
current.Type = record.Type
|
|
current.Admin = record.Admin
|
|
|
|
// add current to map (overwriting previous for this name)
|
|
setUser(current, name)
|
|
|
|
return
|
|
}
|
|
|
|
// EncryptKey encrypts a 16 byte key using the cached key corresponding to name.
|
|
// For AES keys, use the cached key.
|
|
// For RSA keys, the cache is not necessary use the override key instead.
|
|
func EncryptKey(in []byte, name string, override []byte) (out []byte, err error) {
|
|
Refresh()
|
|
|
|
aesKey := override
|
|
|
|
// if the override key is not set, extract from the cache
|
|
if aesKey == nil {
|
|
encryptKey, ok := matchUser(name)
|
|
if !ok {
|
|
return nil, errors.New("Key not delegated")
|
|
}
|
|
|
|
switch encryptKey.Type {
|
|
case passvault.AESRecord:
|
|
aesKey = encryptKey.aesKey
|
|
|
|
default:
|
|
return out, errors.New("Require override for key")
|
|
}
|
|
|
|
useKey(name)
|
|
}
|
|
|
|
// encrypt
|
|
aesSession, err := aes.NewCipher(aesKey)
|
|
if err != nil {
|
|
return
|
|
}
|
|
out = make([]byte, 16)
|
|
aesSession.Encrypt(out, in)
|
|
|
|
return
|
|
}
|
|
|
|
// DecryptKey decrypts a 16 byte key using the key corresponding to the name parameter
|
|
// for AES keys, the cached AES key is used directly to decrypt in
|
|
// for RSA keys, the cached RSA key is used to decrypt the rsaEncryptedKey
|
|
// which is then used to decrypt the input buffer.
|
|
func DecryptKey(in []byte, name string, rsaEncryptedKey []byte) (out []byte, err error) {
|
|
Refresh()
|
|
|
|
decryptKey, ok := matchUser(name)
|
|
if !ok {
|
|
return nil, errors.New("Key not delegated")
|
|
}
|
|
|
|
var aesKey []byte
|
|
|
|
// pick the aesKey to use for decryption
|
|
switch decryptKey.Type {
|
|
case passvault.AESRecord:
|
|
aesKey = decryptKey.aesKey
|
|
|
|
case passvault.RSARecord:
|
|
// extract the aes key from the rsaEncryptedKey
|
|
aesKey, err = rsa.DecryptOAEP(sha1.New(), rand.Reader, &decryptKey.rsaKey, rsaEncryptedKey, nil)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
|
|
default:
|
|
return nil, errors.New("unknown type")
|
|
}
|
|
|
|
// decrypt
|
|
aesSession, err := aes.NewCipher(aesKey)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
out = make([]byte, 16)
|
|
aesSession.Decrypt(out, in)
|
|
|
|
useKey(name)
|
|
|
|
return
|
|
}
|