mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-05-28 10:40:49 +00:00
Merge branch 'master' of https://github.com/jgrahamc/redoctober
Conflicts: src/redoctober/core/core_test.go src/redoctober/passvault/passvault_test.go
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
bin/
|
||||
pkg/
|
||||
src/code.google.com/
|
||||
*~
|
||||
16
Makefile
16
Makefile
@@ -3,17 +3,16 @@ VERSION := 0.1
|
||||
ITERATION := $(shell date +%s)
|
||||
REVISION := $(shell git log -n1 --pretty=format:%h)
|
||||
|
||||
GOPATH := '$(CURDIR)'
|
||||
export GOPATH := $(PWD)
|
||||
|
||||
BUILD_DEPS := go
|
||||
|
||||
.PHONY: all
|
||||
all: $(NAME)
|
||||
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
GOPATH=$(GOPATH) go test $(NAME)/...
|
||||
@go test $(NAME)/...
|
||||
|
||||
.PHONY: print-builddeps
|
||||
print-builddeps:
|
||||
@@ -24,9 +23,8 @@ $(NAME): bin/$(NAME)
|
||||
|
||||
SRC := $(shell find src/$(NAME) -type f)
|
||||
bin/$(NAME): $(SRC)
|
||||
GOPATH=$(GOPATH) go install -tags "$(TAGS)" -ldflags "$(LDFLAGS)" $(NAME)
|
||||
|
||||
|
||||
@go fmt $(NAME)
|
||||
@go install -tags "$(TAGS)" -ldflags "$(LDFLAGS)" $(NAME)
|
||||
|
||||
BUILD_PATH := build
|
||||
INSTALL_PREFIX := usr/local
|
||||
@@ -59,5 +57,7 @@ clean-package:
|
||||
|
||||
.PHONY: clean
|
||||
clean: clean-package
|
||||
GOPATH=$(GOPATH) go clean -i $(NAME)/...
|
||||
$(RM) -r pkg
|
||||
@go clean -i $(NAME)/...
|
||||
@$(RM) -r pkg
|
||||
|
||||
print-%: ; @echo $*=$($*)
|
||||
|
||||
@@ -1,317 +1,310 @@
|
||||
// Pacakge core handles the main operations of the Red October server.
|
||||
// Package core handles the main operations of the Red October server.
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"log"
|
||||
"errors"
|
||||
"encoding/json"
|
||||
"redoctober/passvault"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"redoctober/cryptor"
|
||||
"redoctober/keycache"
|
||||
"redoctober/passvault"
|
||||
)
|
||||
|
||||
// format of incoming sign-in request
|
||||
// Each of these structures corresponds to the JSON expected on the
|
||||
// correspondingly named URI (e.g. the delegate structure maps to the
|
||||
// JSON that should be sent on the /delegate URI and it is handled by
|
||||
// the Delegate function below).
|
||||
|
||||
type create struct {
|
||||
Name string
|
||||
Name string
|
||||
Password string
|
||||
}
|
||||
|
||||
type summary struct {
|
||||
Name string
|
||||
Name string
|
||||
Password string
|
||||
}
|
||||
|
||||
type delegate struct {
|
||||
Name string
|
||||
Name string
|
||||
Password string
|
||||
Uses int
|
||||
Time string
|
||||
|
||||
Uses int
|
||||
Time string
|
||||
}
|
||||
|
||||
type password struct {
|
||||
Name string
|
||||
Name string
|
||||
Password string
|
||||
|
||||
NewPassword string
|
||||
}
|
||||
|
||||
type encrypt struct {
|
||||
Name string
|
||||
Name string
|
||||
Password string
|
||||
Minimum int
|
||||
Owners []string
|
||||
Data []byte
|
||||
|
||||
Minimum int
|
||||
Owners []string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type decrypt struct {
|
||||
Name string
|
||||
Name string
|
||||
Password string
|
||||
Data []byte
|
||||
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type modify struct {
|
||||
Name string
|
||||
Name string
|
||||
Password string
|
||||
|
||||
ToModify string
|
||||
Command string
|
||||
Command string
|
||||
}
|
||||
|
||||
// response JSON format
|
||||
// These structures map the JSON responses that will be sent from the API
|
||||
|
||||
type status struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
type responseData struct {
|
||||
Status string
|
||||
Status string
|
||||
Response []byte
|
||||
}
|
||||
|
||||
type summaryData struct {
|
||||
Status string
|
||||
Live map[string]keycache.ActiveUser
|
||||
All map[string]passvault.Summary
|
||||
Live map[string]keycache.ActiveUser
|
||||
All map[string]passvault.Summary
|
||||
}
|
||||
|
||||
func errToJson(err error) (ret []byte) {
|
||||
if err == nil {
|
||||
ret, _ = json.Marshal(status{Status:"ok"})
|
||||
} else {
|
||||
ret, _ = json.Marshal(status{Status:err.Error()})
|
||||
}
|
||||
return
|
||||
// Helper functions that create JSON responses sent by core
|
||||
|
||||
func jsonStatusOk() ([]byte, error) {
|
||||
return json.Marshal(status{Status: "ok"})
|
||||
}
|
||||
func jsonStatusError(err error) ([]byte, error) {
|
||||
return json.Marshal(status{Status: err.Error()})
|
||||
}
|
||||
func jsonSummary() ([]byte, error) {
|
||||
return json.Marshal(summaryData{Status: "ok",Live: keycache.GetSummary(), All: passvault.GetSummary()})
|
||||
}
|
||||
func jsonResponse(resp []byte) ([]byte, error) {
|
||||
return json.Marshal(responseData{Status: "ok", Response: resp})
|
||||
}
|
||||
|
||||
func summaryToJson(err error) (ret []byte) {
|
||||
if err == nil {
|
||||
ret, _ = json.Marshal(summaryData{Status:"ok", Live:keycache.GetSummary(), All:passvault.GetSummary()})
|
||||
} else {
|
||||
ret, _ = json.Marshal(status{Status:err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func responseToJson(resp []byte, err error) (ret []byte) {
|
||||
if err == nil {
|
||||
ret, _ = json.Marshal(responseData{Status:"ok", Response:resp})
|
||||
} else {
|
||||
ret, _ = json.Marshal(status{Status:err.Error()})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func validateAdmin(name string, password string) (err error) {
|
||||
// validateAdmin checks that the username and password passed in are
|
||||
// correct and that the user is an admin
|
||||
func validateAdmin(name, password string) error {
|
||||
if passvault.NumRecords() == 0 {
|
||||
return errors.New("Vault is not created yet")
|
||||
}
|
||||
|
||||
// find record
|
||||
passwordRec, ok := passvault.GetRecord(name)
|
||||
pr, ok := passvault.GetRecord(name)
|
||||
if !ok {
|
||||
return errors.New("User not present")
|
||||
}
|
||||
err = passwordRec.ValidatePassword(password)
|
||||
if err != nil {
|
||||
return
|
||||
if err := pr.ValidatePassword(password); err != nil {
|
||||
return err
|
||||
}
|
||||
if !passwordRec.IsAdmin() {
|
||||
if !pr.IsAdmin() {
|
||||
return errors.New("Admin required")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Init reads the records from disk from a given path
|
||||
func Init(path string) (err error) {
|
||||
if err = passvault.InitFromDisk(path); err != nil {
|
||||
err = fmt.Errorf("Failed to load password vault %s: %s", path, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Init reads the records from disk from a given path.
|
||||
func Init(path string) {
|
||||
passvault.InitFromDisk(path)
|
||||
}
|
||||
|
||||
// Create processes a create request.
|
||||
func Create(jsonIn []byte) []byte {
|
||||
func Create(jsonIn []byte) ([]byte, error) {
|
||||
var s create
|
||||
err := json.Unmarshal(jsonIn, &s)
|
||||
if err != nil {
|
||||
return errToJson(err)
|
||||
if err := json.Unmarshal(jsonIn, &s); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
if passvault.NumRecords() != 0 {
|
||||
return errToJson(errors.New("Vault is already created"))
|
||||
return jsonStatusError(errors.New("Vault is already created"))
|
||||
}
|
||||
|
||||
if _, err := passvault.AddNewRecord(s.Name, s.Password, true); err != nil {
|
||||
log.Printf("Error adding record for %s: %s\n", s.Name, err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
_, err = passvault.AddNewRecord(s.Name, s.Password, true)
|
||||
if err != nil {
|
||||
log.Println("Error adding record:", err)
|
||||
return errToJson(err)
|
||||
}
|
||||
|
||||
return errToJson(err)
|
||||
return jsonStatusOk()
|
||||
}
|
||||
|
||||
// Summary processes a summary request.
|
||||
func Summary(jsonIn []byte) []byte {
|
||||
func Summary(jsonIn []byte) ([]byte, error) {
|
||||
var s summary
|
||||
keycache.Refresh()
|
||||
|
||||
err := json.Unmarshal(jsonIn, &s)
|
||||
if err != nil {
|
||||
return errToJson(err)
|
||||
if err := json.Unmarshal(jsonIn, &s); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
if passvault.NumRecords() == 0 {
|
||||
return errToJson(errors.New("Vault is not created yet"))
|
||||
return jsonStatusError(errors.New("Vault is not created yet"))
|
||||
}
|
||||
|
||||
// validate admin
|
||||
err = validateAdmin(s.Name, s.Password)
|
||||
if err != nil {
|
||||
log.Println("Error validating admin status", err)
|
||||
return errToJson(err)
|
||||
if err := validateAdmin(s.Name, s.Password); err != nil {
|
||||
log.Printf("Error validating admin status of %s: %s", s.Name, err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
// populate
|
||||
return summaryToJson(err)
|
||||
return jsonSummary()
|
||||
}
|
||||
|
||||
// Delegate processes a delegation request.
|
||||
func Delegate(jsonIn []byte) []byte {
|
||||
func Delegate(jsonIn []byte) ([]byte, error) {
|
||||
var s delegate
|
||||
err := json.Unmarshal(jsonIn, &s)
|
||||
if err != nil {
|
||||
return errToJson(err)
|
||||
if err := json.Unmarshal(jsonIn, &s); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
if passvault.NumRecords() == 0 {
|
||||
return errToJson(errors.New("Vault is not created yet"))
|
||||
return jsonStatusError(errors.New("Vault is not created yet"))
|
||||
}
|
||||
|
||||
// find record
|
||||
passwordRec, ok := passvault.GetRecord(s.Name)
|
||||
if ok {
|
||||
err = passwordRec.ValidatePassword(s.Password)
|
||||
if err != nil {
|
||||
return errToJson(err)
|
||||
// Find password record for user and verify that their password
|
||||
// matches. If not found then add a new entry for this user.
|
||||
|
||||
pr, found := passvault.GetRecord(s.Name)
|
||||
if found {
|
||||
if err := pr.ValidatePassword(s.Password); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
} else {
|
||||
passwordRec, err = passvault.AddNewRecord(s.Name, s.Password, false)
|
||||
if err != nil {
|
||||
log.Println("Error adding record:", err)
|
||||
return errToJson(err)
|
||||
var err error
|
||||
if pr, err = passvault.AddNewRecord(s.Name, s.Password, false); err != nil {
|
||||
log.Printf("Error adding record for %s: %s\n", s.Name, err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// add signed-in record to active set
|
||||
err = keycache.AddKeyFromRecord(passwordRec, s.Name, s.Password, s.Uses, s.Time)
|
||||
if err != nil {
|
||||
log.Println("Error adding key to cache:", err)
|
||||
return errToJson(err)
|
||||
if err := keycache.AddKeyFromRecord(pr, s.Name, s.Password, s.Uses, s.Time); err != nil {
|
||||
log.Printf("Error adding key to cache for %s: %s\n", s.Name, err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
return errToJson(err)
|
||||
return jsonStatusOk()
|
||||
}
|
||||
|
||||
// Password processes a password change request.
|
||||
func Password(jsonIn []byte) []byte {
|
||||
func Password(jsonIn []byte) ([]byte, error) {
|
||||
var s password
|
||||
err := json.Unmarshal(jsonIn, &s)
|
||||
if err != nil {
|
||||
return errToJson(err)
|
||||
if err := json.Unmarshal(jsonIn, &s); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
if passvault.NumRecords() == 0 {
|
||||
return errToJson(errors.New("Vault is not created yet"))
|
||||
return jsonStatusError(errors.New("Vault is not created yet"))
|
||||
}
|
||||
|
||||
// add signed-in record to active set
|
||||
err = passvault.ChangePassword(s.Name, s.Password, s.NewPassword)
|
||||
if err != nil {
|
||||
if err := passvault.ChangePassword(s.Name, s.Password, s.NewPassword); err != nil {
|
||||
log.Println("Error changing password:", err)
|
||||
return errToJson(err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
return errToJson(err)
|
||||
return jsonStatusOk()
|
||||
}
|
||||
|
||||
// Encrypt processes an encrypt request.
|
||||
func Encrypt(jsonIn []byte) (ret []byte) {
|
||||
func Encrypt(jsonIn []byte) ([]byte, error) {
|
||||
var s encrypt
|
||||
err := json.Unmarshal(jsonIn, &s)
|
||||
if err != nil {
|
||||
return errToJson(err)
|
||||
if err := json.Unmarshal(jsonIn, &s); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
err = validateAdmin(s.Name, s.Password)
|
||||
if err != nil {
|
||||
if err := validateAdmin(s.Name, s.Password); err != nil {
|
||||
log.Println("Error validating admin status", err)
|
||||
return errToJson(err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
// Encrypt file with list of owners
|
||||
resp, err := cryptor.Encrypt(s.Data, s.Owners, s.Minimum)
|
||||
if err != nil {
|
||||
if resp, err := cryptor.Encrypt(s.Data, s.Owners, s.Minimum); err != nil {
|
||||
log.Println("Error encrypting:", err)
|
||||
return errToJson(err)
|
||||
return jsonStatusError(err)
|
||||
} else {
|
||||
return jsonResponse(resp)
|
||||
}
|
||||
|
||||
return responseToJson(resp, err)
|
||||
}
|
||||
|
||||
// Decrypt processes a decrypt request.
|
||||
func Decrypt(jsonIn []byte) (ret []byte) {
|
||||
func Decrypt(jsonIn []byte) ([]byte, error) {
|
||||
var s decrypt
|
||||
err := json.Unmarshal(jsonIn, &s)
|
||||
if err != nil {
|
||||
return errToJson(err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
err = validateAdmin(s.Name, s.Password)
|
||||
if err != nil {
|
||||
log.Println("Error validating admin status", err)
|
||||
return errToJson(err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
resp, err := cryptor.Decrypt(s.Data)
|
||||
if err != nil {
|
||||
log.Println("Error decrypting:", err)
|
||||
return errToJson(err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
return responseToJson(resp, err)
|
||||
return jsonResponse(resp)
|
||||
}
|
||||
|
||||
// Modify processes a modify request.
|
||||
func Modify(jsonIn []byte) []byte {
|
||||
func Modify(jsonIn []byte) ([]byte, error) {
|
||||
var s modify
|
||||
|
||||
err := json.Unmarshal(jsonIn, &s)
|
||||
if err != nil {
|
||||
return errToJson(err)
|
||||
if err := json.Unmarshal(jsonIn, &s); err != nil {
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
err = validateAdmin(s.Name, s.Password)
|
||||
if err != nil {
|
||||
log.Println("Error validating admin status", err)
|
||||
return errToJson(err)
|
||||
if err := validateAdmin(s.Name, s.Password); err != nil {
|
||||
log.Printf("Error validating admin status of %s: %s", s.Name, err)
|
||||
return jsonStatusError(err)
|
||||
}
|
||||
|
||||
if _, ok := passvault.GetRecord(s.ToModify); !ok {
|
||||
return errToJson(errors.New("Record to modify missing"))
|
||||
return jsonStatusError(errors.New("Record to modify missing"))
|
||||
}
|
||||
|
||||
if s.Name == s.ToModify {
|
||||
return errToJson(errors.New("Cannot modify own record"))
|
||||
return jsonStatusError(errors.New("Cannot modify own record"))
|
||||
}
|
||||
switch s.Command {
|
||||
case "delete": {
|
||||
err = passvault.DeleteRecord(s.ToModify)
|
||||
}
|
||||
case "revoke": {
|
||||
err = passvault.RevokeRecord(s.ToModify)
|
||||
}
|
||||
case "admin": {
|
||||
err = passvault.MakeAdmin(s.ToModify)
|
||||
}
|
||||
default: {
|
||||
return errToJson(errors.New("Unknown command"))
|
||||
}
|
||||
}
|
||||
return errToJson(err)
|
||||
}
|
||||
|
||||
var err error
|
||||
switch s.Command {
|
||||
case "delete":
|
||||
err = passvault.DeleteRecord(s.ToModify)
|
||||
case "revoke":
|
||||
err = passvault.RevokeRecord(s.ToModify)
|
||||
case "admin":
|
||||
err = passvault.MakeAdmin(s.ToModify)
|
||||
default:
|
||||
return jsonStatusError(errors.New("Unknown command"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return jsonStatusError(err)
|
||||
} else {
|
||||
return jsonStatusOk()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
// core_test.go: tests for core.go
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"os"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"os"
|
||||
"redoctober/passvault"
|
||||
"redoctober/keycache"
|
||||
"redoctober/keycache"
|
||||
"redoctober/passvault"
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
createJson := []byte("{\"Name\":\"Alice\",\"Password\":\"Hello\"}")
|
||||
|
||||
|
||||
os.Remove("/tmp/db1.json")
|
||||
Init("/tmp/db1.json")
|
||||
|
||||
respJson := Create(createJson)
|
||||
respJson, err := Create(createJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account, ", err)
|
||||
}
|
||||
|
||||
var s responseData
|
||||
err := json.Unmarshal(respJson, &s)
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account, ", err)
|
||||
}
|
||||
@@ -26,7 +35,10 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
// check to see if creation can happen twice
|
||||
respJson = Create(createJson)
|
||||
respJson, err = Create(createJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account when one exists, ", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account when one exists, ", err)
|
||||
@@ -44,9 +56,12 @@ func TestSummary(t *testing.T) {
|
||||
os.Remove("/tmp/db1.json")
|
||||
|
||||
// check for summary of uninitialized vault
|
||||
respJson := Summary(createJson)
|
||||
respJson, err := Summary(createJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary of account with no vault,", err)
|
||||
}
|
||||
var s summaryData
|
||||
err := json.Unmarshal(respJson, &s)
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary of account with no vault,", err)
|
||||
}
|
||||
@@ -57,7 +72,10 @@ func TestSummary(t *testing.T) {
|
||||
Init("/tmp/db1.json")
|
||||
|
||||
// check for summary of initialized vault
|
||||
respJson = Create(createJson)
|
||||
respJson, err = Create(createJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account, ", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account, ", err)
|
||||
@@ -66,7 +84,10 @@ func TestSummary(t *testing.T) {
|
||||
t.Fatalf("Error in creating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Summary(createJson)
|
||||
respJson, err = Summary(createJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary of account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary of account,", err)
|
||||
@@ -87,7 +108,10 @@ func TestSummary(t *testing.T) {
|
||||
}
|
||||
|
||||
// check for summary of initialized vault with new member
|
||||
respJson = Delegate(delegateJson)
|
||||
respJson, err = Delegate(delegateJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -96,7 +120,10 @@ func TestSummary(t *testing.T) {
|
||||
t.Fatalf("Error in delegating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Summary(createJson)
|
||||
respJson, err = Summary(createJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary of account with no vault,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary of account with no vault,", err)
|
||||
@@ -138,7 +165,6 @@ func TestSummary(t *testing.T) {
|
||||
t.Fatalf("Error in summary of account, record missing ")
|
||||
}
|
||||
|
||||
//
|
||||
keycache.FlushCache()
|
||||
|
||||
os.Remove("/tmp/db1.json")
|
||||
@@ -156,8 +182,11 @@ func TestPassword(t *testing.T) {
|
||||
|
||||
// check for summary of initialized vault with new member
|
||||
var s responseData
|
||||
respJson := Create(createJson)
|
||||
err := json.Unmarshal(respJson, &s)
|
||||
respJson, err := Create(createJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account, ", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account, ", err)
|
||||
}
|
||||
@@ -165,7 +194,10 @@ func TestPassword(t *testing.T) {
|
||||
t.Fatalf("Error in creating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Delegate(delegateJson)
|
||||
respJson, err = Delegate(delegateJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -174,7 +206,10 @@ func TestPassword(t *testing.T) {
|
||||
t.Fatalf("Error in delegating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Password(passwordJson2)
|
||||
respJson, err = Password(passwordJson2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in password", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in password", err)
|
||||
@@ -183,7 +218,10 @@ func TestPassword(t *testing.T) {
|
||||
t.Fatalf("Error in password, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Password(passwordJson)
|
||||
respJson, err = Password(passwordJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in password", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in password", err)
|
||||
@@ -192,7 +230,10 @@ func TestPassword(t *testing.T) {
|
||||
t.Fatalf("Error in password, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Delegate(delegateJson)
|
||||
respJson, err = Delegate(delegateJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -201,7 +242,10 @@ func TestPassword(t *testing.T) {
|
||||
t.Fatalf("Error in delegating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Delegate(delegateJson2)
|
||||
respJson, err = Delegate(delegateJson2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -210,7 +254,10 @@ func TestPassword(t *testing.T) {
|
||||
t.Fatalf("Error in delegating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Password(passwordJson2)
|
||||
respJson, err = Password(passwordJson2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in password", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in password", err)
|
||||
@@ -219,7 +266,10 @@ func TestPassword(t *testing.T) {
|
||||
t.Fatalf("Error in password, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Delegate(delegateJson)
|
||||
respJson, err = Delegate(delegateJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -248,16 +298,22 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
|
||||
// check for summary of initialized vault with new member
|
||||
var s responseData
|
||||
respJson := Create(delegateJson)
|
||||
err := json.Unmarshal(respJson, &s)
|
||||
respJson, err := Create(delegateJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account,", err)
|
||||
}
|
||||
if s.Status != "ok" {
|
||||
t.Fatalf("Error in creating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson, err = Delegate(delegateJson2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
if s.Status != "ok" {
|
||||
t.Fatalf("Error in delegating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Delegate(delegateJson2)
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -266,7 +322,10 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
t.Fatalf("Error in delegating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Delegate(delegateJson3)
|
||||
respJson, err = Delegate(delegateJson3)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -277,7 +336,10 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
|
||||
// check summary to see if none are delegated
|
||||
keycache.Refresh()
|
||||
respJson = Summary(summaryJson)
|
||||
respJson, err = Summary(summaryJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
}
|
||||
var sum summaryData
|
||||
err = json.Unmarshal(respJson, &sum)
|
||||
if err != nil {
|
||||
@@ -291,7 +353,10 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
}
|
||||
|
||||
// Encrypt with non-admin (fail)
|
||||
respJson = Encrypt(encryptJson)
|
||||
respJson, err = Encrypt(encryptJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in encrypt,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in encrypt,", err)
|
||||
@@ -301,7 +366,10 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
}
|
||||
|
||||
// Encrypt
|
||||
respJson = Encrypt(encryptJson2)
|
||||
respJson, err = Encrypt(encryptJson2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in encrypt,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in encrypt,", err)
|
||||
@@ -310,14 +378,16 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
t.Fatalf("Error in encrypt, ", s.Status)
|
||||
}
|
||||
|
||||
|
||||
// decrypt file
|
||||
decryptJson, err := json.Marshal(decrypt{Name:"Alice", Password:"Hello", Data:s.Response})
|
||||
if err != nil {
|
||||
t.Fatalf("Error in marshalling decryption,", err)
|
||||
}
|
||||
|
||||
respJson2 := Decrypt(decryptJson)
|
||||
respJson2, err := Decrypt(decryptJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in decrypt,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson2, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in decrypt,", err)
|
||||
@@ -327,7 +397,10 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
}
|
||||
|
||||
// delegate two valid decryptors
|
||||
respJson = Delegate(delegateJson4)
|
||||
respJson, err = Delegate(delegateJson4)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -336,7 +409,10 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
t.Fatalf("Error in delegating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Delegate(delegateJson5)
|
||||
respJson, err = Delegate(delegateJson5)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -348,7 +424,10 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
// verify the presence of the two delgations
|
||||
keycache.Refresh()
|
||||
var sum2 summaryData
|
||||
respJson = Summary(summaryJson)
|
||||
respJson, err = Summary(summaryJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &sum2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
@@ -360,7 +439,10 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
t.Fatalf("Error in summary, ", sum2.Live)
|
||||
}
|
||||
|
||||
respJson2 = Decrypt(decryptJson)
|
||||
respJson2, err = Decrypt(decryptJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in decrypt,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson2, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in decrypt,", err)
|
||||
@@ -394,16 +476,22 @@ func TestModify(t *testing.T) {
|
||||
|
||||
// check for summary of initialized vault with new member
|
||||
var s responseData
|
||||
respJson := Create(delegateJson)
|
||||
err := json.Unmarshal(respJson, &s)
|
||||
respJson, err := Create(delegateJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in creating account,", err)
|
||||
}
|
||||
if s.Status != "ok" {
|
||||
t.Fatalf("Error in creating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson, err = Delegate(delegateJson2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
if s.Status != "ok" {
|
||||
t.Fatalf("Error in delegating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Delegate(delegateJson2)
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -412,7 +500,10 @@ func TestModify(t *testing.T) {
|
||||
t.Fatalf("Error in delegating account, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Delegate(delegateJson3)
|
||||
respJson, err = Delegate(delegateJson3)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in delegating account,", err)
|
||||
@@ -423,7 +514,10 @@ func TestModify(t *testing.T) {
|
||||
|
||||
// check summary to see if none are delegated
|
||||
keycache.Refresh()
|
||||
respJson = Summary(summaryJson)
|
||||
respJson, err = Summary(summaryJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
}
|
||||
var sum summaryData
|
||||
err = json.Unmarshal(respJson, &sum)
|
||||
if err != nil {
|
||||
@@ -437,7 +531,10 @@ func TestModify(t *testing.T) {
|
||||
}
|
||||
|
||||
// Modify from non-admin (fail)
|
||||
respJson = Modify(modifyJson)
|
||||
respJson, err = Modify(modifyJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
@@ -447,7 +544,10 @@ func TestModify(t *testing.T) {
|
||||
}
|
||||
|
||||
// Modify self from admin (fail)
|
||||
respJson = Modify(modifyJson2)
|
||||
respJson, err = Modify(modifyJson2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
@@ -457,7 +557,10 @@ func TestModify(t *testing.T) {
|
||||
}
|
||||
|
||||
// Modify admin from admin
|
||||
respJson = Modify(modifyJson3)
|
||||
respJson, err = Modify(modifyJson3)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
@@ -466,7 +569,10 @@ func TestModify(t *testing.T) {
|
||||
t.Fatalf("Error in modify, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Summary(summaryJson)
|
||||
respJson, err = Summary(summaryJson)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &sum)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
@@ -479,7 +585,10 @@ func TestModify(t *testing.T) {
|
||||
}
|
||||
|
||||
// Revoke admin from admin
|
||||
respJson = Modify(modifyJson4)
|
||||
respJson, err = Modify(modifyJson4)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
@@ -488,7 +597,10 @@ func TestModify(t *testing.T) {
|
||||
t.Fatalf("Error in modify, ", s.Status)
|
||||
}
|
||||
|
||||
respJson = Summary(summaryJson2)
|
||||
respJson, err = Summary(summaryJson2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &sum)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
@@ -501,7 +613,10 @@ func TestModify(t *testing.T) {
|
||||
}
|
||||
|
||||
// Delete from admin
|
||||
respJson = Modify(modifyJson5)
|
||||
respJson, err = Modify(modifyJson5)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &s)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in modify,", err)
|
||||
@@ -511,7 +626,10 @@ func TestModify(t *testing.T) {
|
||||
}
|
||||
|
||||
var sum3 summaryData
|
||||
respJson = Summary(summaryJson2)
|
||||
respJson, err = Summary(summaryJson2)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
}
|
||||
err = json.Unmarshal(respJson, &sum3)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in summary,", err)
|
||||
@@ -527,4 +645,3 @@ func TestModify(t *testing.T) {
|
||||
|
||||
os.Remove("/tmp/db1.json")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +1,51 @@
|
||||
// Package cryptor encrypts and decrypts files using the Red October vault and key cache.
|
||||
// Package cryptor encrypts and decrypts files using the Red October
|
||||
// vault and key cache.
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package cryptor
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/rand"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"redoctober/passvault"
|
||||
"redoctober/keycache"
|
||||
"redoctober/padding"
|
||||
"redoctober/passvault"
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_VERSION = 1
|
||||
)
|
||||
|
||||
// MultiWrappedKey is a structure containing
|
||||
// a 16-byte key encrypted once for each of the keys corresponding to
|
||||
// the names of the users in Name in order.
|
||||
// MultiWrappedKey is a structure containing a 16-byte key encrypted
|
||||
// once for each of the keys corresponding to the names of the users
|
||||
// in Name in order.
|
||||
type MultiWrappedKey struct {
|
||||
Name []string
|
||||
Key []byte
|
||||
Key []byte
|
||||
}
|
||||
|
||||
// SingleWrappedKey is a structure containing
|
||||
// a 16-byte key encrypted by an RSA key.
|
||||
// SingleWrappedKey is a structure containing a 16-byte key encrypted
|
||||
// by an RSA key.
|
||||
type SingleWrappedKey struct {
|
||||
Key []byte
|
||||
Key []byte
|
||||
aesKey []byte
|
||||
}
|
||||
|
||||
// EncryptedFile is the format for encrypted data containing all the keys necessary to
|
||||
// decrypt it when delegated.
|
||||
// EncryptedFile is the format for encrypted data containing all the
|
||||
// keys necessary to decrypt it when delegated.
|
||||
type EncryptedFile struct {
|
||||
Version int
|
||||
VaultId int
|
||||
KeySet []MultiWrappedKey
|
||||
Version int
|
||||
VaultId int
|
||||
KeySet []MultiWrappedKey
|
||||
KeySetRSA map[string]SingleWrappedKey
|
||||
IV []byte
|
||||
Data []byte
|
||||
IV []byte
|
||||
Data []byte
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
@@ -56,7 +60,8 @@ func makeRandom(length int) (bytes []byte, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// encrypt clearKey with the key associated with name inner, then name outer
|
||||
// encrypt clearKey with the key associated with name inner, then name
|
||||
// outer
|
||||
func encryptKey(nameInner, nameOuter string, clearKey []byte, rsaKeys map[string]SingleWrappedKey) (out MultiWrappedKey, err error) {
|
||||
out.Name = []string{nameOuter, nameInner}
|
||||
|
||||
@@ -90,7 +95,7 @@ func encryptKey(nameInner, nameOuter string, clearKey []byte, rsaKeys map[string
|
||||
err = errors.New("Missing user in file")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
overrideOuter, ok = rsaKeys[nameOuter]
|
||||
if !ok {
|
||||
err = errors.New("Missing user in file")
|
||||
@@ -105,12 +110,10 @@ func encryptKey(nameInner, nameOuter string, clearKey []byte, rsaKeys map[string
|
||||
}
|
||||
|
||||
// double-wrap the keys
|
||||
keyBytes, err = keycache.EncryptKey(clearKey, nameInner, overrideInner.aesKey)
|
||||
if err != nil {
|
||||
if keyBytes, err = keycache.EncryptKey(clearKey, nameInner, overrideInner.aesKey); err != nil {
|
||||
return out, err
|
||||
}
|
||||
keyBytes, err = keycache.EncryptKey(keyBytes, nameOuter, overrideOuter.aesKey)
|
||||
if err != nil {
|
||||
if keyBytes, err = keycache.EncryptKey(keyBytes, nameOuter, overrideOuter.aesKey); err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
@@ -122,7 +125,7 @@ func encryptKey(nameInner, nameOuter string, clearKey []byte, rsaKeys map[string
|
||||
// decrypt first key in keys whose encryption keys are in keycache
|
||||
func unwrapKey(keys []MultiWrappedKey, rsaKeys map[string]SingleWrappedKey) (unwrappedKey []byte, err error) {
|
||||
var (
|
||||
keyFound error
|
||||
keyFound error
|
||||
fullMatch bool = false
|
||||
)
|
||||
for _, mwKey := range keys {
|
||||
@@ -153,22 +156,21 @@ func unwrapKey(keys []MultiWrappedKey, rsaKeys map[string]SingleWrappedKey) (unw
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Encrypt encrypts data with the keys associated with names
|
||||
// This requires a minimum of min keys to decrypt.
|
||||
// NOTE: as currently implemented, the maximum value for min is 2.
|
||||
func Encrypt(in []byte, names []string, min int) (resp []byte, err error) {
|
||||
// decode data to encrypt
|
||||
clearFile := padding.PadClearFile(in)
|
||||
if min > 2 {
|
||||
return nil, errors.New("Minimum restricted to 2")
|
||||
}
|
||||
|
||||
// decode data to encrypt
|
||||
clearFile := padding.AddPadding(in)
|
||||
|
||||
// set up encrypted data structure
|
||||
var encrypted EncryptedFile
|
||||
encrypted.Version = DEFAULT_VERSION
|
||||
encrypted.VaultId, err = passvault.GetVaultId()
|
||||
if err != nil {
|
||||
if encrypted.VaultId, err = passvault.GetVaultId(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -182,14 +184,14 @@ func Encrypt(in []byte, names []string, min int) (resp []byte, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// allocate set of keys to be able to cover all ordered subsets
|
||||
// of length 2 of names
|
||||
encrypted.KeySet = make([]MultiWrappedKey, len(names)*(len(names)-1))
|
||||
|
||||
// create map to hold RSA encrypted keys
|
||||
encrypted.KeySetRSA = make(map[string]SingleWrappedKey)
|
||||
|
||||
|
||||
var singleWrappedKey SingleWrappedKey
|
||||
for _, name := range names {
|
||||
rec, ok := passvault.GetRecord(name)
|
||||
@@ -257,8 +259,7 @@ func Encrypt(in []byte, names []string, min int) (resp []byte, err error) {
|
||||
func Decrypt(in []byte) (resp []byte, err error) {
|
||||
// unwrap encrypted file
|
||||
var encrypted EncryptedFile
|
||||
err = json.Unmarshal(in, &encrypted)
|
||||
if err != nil {
|
||||
if err = json.Unmarshal(in, &encrypted); err != nil {
|
||||
return
|
||||
}
|
||||
if encrypted.Version != DEFAULT_VERSION {
|
||||
@@ -296,11 +297,10 @@ func Decrypt(in []byte) (resp []byte, err error) {
|
||||
|
||||
// decrypt file key with delegate keys
|
||||
var unwrappedKey = make([]byte, 16)
|
||||
unwrappedKey, err = unwrapKey(encrypted.KeySet, encrypted.KeySetRSA)
|
||||
if err != nil {
|
||||
if unwrappedKey, err = unwrapKey(encrypted.KeySet, encrypted.KeySetRSA); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// set up the decryption context
|
||||
aesCrypt, err := aes.NewCipher(unwrappedKey)
|
||||
if err != nil {
|
||||
@@ -314,4 +314,3 @@ func Decrypt(in []byte) (resp []byte, err error) {
|
||||
|
||||
return padding.RemovePadding(clearData)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
// Package keycache provides the ability to hold active keys in memory
|
||||
// for the Red October server.
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package keycache
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
"errors"
|
||||
"crypto/aes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"log"
|
||||
"redoctober/passvault"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UserKeys is the set of decrypted keys in memory, indexed by name.
|
||||
@@ -18,11 +21,11 @@ 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
|
||||
Admin bool
|
||||
Type string
|
||||
Expiry time.Time
|
||||
Uses int
|
||||
// non-public members
|
||||
Uses int
|
||||
|
||||
aesKey []byte
|
||||
rsaKey rsa.PrivateKey
|
||||
}
|
||||
@@ -38,8 +41,7 @@ func setUser(in ActiveUser, name string) {
|
||||
|
||||
// mark a use of the key, only for decryption or symmetric encryption
|
||||
func useKey(name string) {
|
||||
val, present := matchUser(name)
|
||||
if present {
|
||||
if val, present := matchUser(name); present {
|
||||
val.Uses -= 1
|
||||
setUser(val, name)
|
||||
}
|
||||
@@ -68,7 +70,7 @@ func Refresh() {
|
||||
}
|
||||
|
||||
// AddKeyFromRecord decrypts a key for a given record and adds it to the cache.
|
||||
func AddKeyFromRecord(record passvault.DiskPasswordRecord, name string, password string, uses int, durationString string) (err error) {
|
||||
func AddKeyFromRecord(record passvault.PasswordRecord, name string, password string, uses int, durationString string) (err error) {
|
||||
var current ActiveUser
|
||||
|
||||
Refresh()
|
||||
@@ -184,4 +186,3 @@ func DecryptKey(in []byte, name string, rsaEncryptedKey []byte) (out []byte, err
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// keycache_test.go: tests for keycache.go
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
package keycache
|
||||
|
||||
import (
|
||||
"passvault"
|
||||
"time"
|
||||
"redoctober/passvault"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var now = time.Now()
|
||||
@@ -13,10 +16,10 @@ var dummy = make([]byte, 16)
|
||||
|
||||
func TestUsesFlush(t *testing.T) {
|
||||
singleUse := ActiveUser{
|
||||
Admin: true,
|
||||
Type: passvault.AESRecord,
|
||||
Admin: true,
|
||||
Type: passvault.AESRecord,
|
||||
Expiry: nextYear,
|
||||
Uses: 2,
|
||||
Uses: 2,
|
||||
aesKey: emptyKey,
|
||||
}
|
||||
|
||||
@@ -47,10 +50,10 @@ func TestTimeFlush(t *testing.T) {
|
||||
one := now.Add(oneSec)
|
||||
|
||||
singleUse := ActiveUser{
|
||||
Admin: true,
|
||||
Type: passvault.AESRecord,
|
||||
Admin: true,
|
||||
Type: passvault.AESRecord,
|
||||
Expiry: one,
|
||||
Uses: 10,
|
||||
Uses: 10,
|
||||
aesKey: emptyKey,
|
||||
}
|
||||
|
||||
@@ -76,5 +79,3 @@ func TestTimeFlush(t *testing.T) {
|
||||
t.Fatalf("Error in pruning expired key")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,29 +1,46 @@
|
||||
// Package padding adds and removes padding for AES-CBC mode.
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package padding
|
||||
|
||||
import "errors"
|
||||
|
||||
// RemovePadding removes padding from clear data.
|
||||
func RemovePadding(bytesPadded []byte) ([]byte, error) {
|
||||
// last byte is padding byte
|
||||
paddingLen := int(bytesPadded[len(bytesPadded) - 1])
|
||||
if paddingLen > 16 {
|
||||
// The final byte of a padded []byte indicates the number of padding
|
||||
// bytes that were added. The padding bytes are always NUL bytes and
|
||||
// up to 16 bytes may be added.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// 1. Data to be padded has a length divisible by 16. 16 bytes will be
|
||||
// added where the first 15 are 0x00 and the final byte is 0x10.
|
||||
//
|
||||
// 2. Data to be padded has a length with remainder 15 when divided by
|
||||
// 16. One byte will be added and that byte will be 0x01 (indicating
|
||||
// one byte of padding).
|
||||
//
|
||||
// 3. Data to be padded has a length with remainder 2 when divided by
|
||||
// 16. 14 bytes will be added. The first 13 will be 0x00 and then final
|
||||
// byte will be 0x0e.
|
||||
//
|
||||
// Removing padding is trivial: the number of bytes specified by the
|
||||
// final byte are removed.
|
||||
|
||||
// RemovePadding removes padding from data that was added with
|
||||
// AddPadding
|
||||
func RemovePadding(b []byte) ([]byte, error) {
|
||||
l := int(b[len(b)-1])
|
||||
if l > 16 {
|
||||
return nil, errors.New("Padding incorrect")
|
||||
}
|
||||
fileLen := len(bytesPadded) - paddingLen
|
||||
|
||||
return bytesPadded[:fileLen], nil
|
||||
return b[:len(b)-l], nil
|
||||
}
|
||||
|
||||
// PadClearFile adds padding to clear file.
|
||||
func PadClearFile(fileBytes []byte) (paddedFile []byte) {
|
||||
// pad with zeros, last byte is the size of padding
|
||||
paddingLen := 16 - len(fileBytes) % 16
|
||||
padding := make([]byte, paddingLen)
|
||||
padding[paddingLen - 1] = byte(paddingLen)
|
||||
paddedFile = append(fileBytes, padding...)
|
||||
|
||||
return
|
||||
// AddPadding adds padding to a block of data
|
||||
func AddPadding(b []byte) []byte {
|
||||
l := 16 - len(b)%16
|
||||
padding := make([]byte, l)
|
||||
padding[l-1] = byte(l)
|
||||
return append(b, padding...)
|
||||
}
|
||||
|
||||
|
||||
|
||||
73
src/redoctober/padding/padding_test.go
Normal file
73
src/redoctober/padding/padding_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// padding_test.go: tests for padding.go
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package padding
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assert(t *testing.T, b bool) {
|
||||
if !b {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddPadding(t *testing.T) {
|
||||
b := make([]byte, 16)
|
||||
c := AddPadding(b)
|
||||
assert(t, len(c) == 32)
|
||||
assert(t, c[31] == 16)
|
||||
|
||||
b = make([]byte, 32)
|
||||
c = AddPadding(b)
|
||||
assert(t, len(c) == 48)
|
||||
assert(t, c[47] == 16)
|
||||
|
||||
b = make([]byte, 1)
|
||||
c = AddPadding(b)
|
||||
assert(t, len(c) == 16)
|
||||
assert(t, c[15] == 15)
|
||||
|
||||
b = make([]byte, 15)
|
||||
c = AddPadding(b)
|
||||
assert(t, len(c) == 16)
|
||||
assert(t, c[15] == 1)
|
||||
}
|
||||
|
||||
func TestRemovePadding(t *testing.T) {
|
||||
b := []byte("0123456789ABCDEF")
|
||||
c := AddPadding(b)
|
||||
assert(t, len(c) == 32)
|
||||
assert(t, c[31] == 16)
|
||||
assert(t, bytes.Compare(c[:16], b[:16]) == 0)
|
||||
d, err := RemovePadding(c)
|
||||
assert(t, err == nil)
|
||||
assert(t, len(d) == 16)
|
||||
assert(t, bytes.Compare(b, d) == 0)
|
||||
|
||||
b = []byte("0123456789")
|
||||
c = AddPadding(b)
|
||||
assert(t, len(c) == 16)
|
||||
assert(t, c[15] == 6)
|
||||
assert(t, bytes.Compare(c[:10], b[:10]) == 0)
|
||||
d, err = RemovePadding(c)
|
||||
assert(t, err == nil)
|
||||
assert(t, len(d) == 10)
|
||||
assert(t, bytes.Compare(b, d) == 0)
|
||||
}
|
||||
|
||||
func TestDetectBadPadding(t *testing.T) {
|
||||
b := []byte("0123456789ABCDEF")
|
||||
c := AddPadding(b)
|
||||
assert(t, len(c) == 32)
|
||||
assert(t, c[31] == 16)
|
||||
assert(t, bytes.Compare(c[:16], b[:16]) == 0)
|
||||
c[31] = 42
|
||||
d, err := RemovePadding(c)
|
||||
assert(t, err != nil)
|
||||
assert(t, d == nil)
|
||||
}
|
||||
|
||||
@@ -1,154 +1,151 @@
|
||||
// Package passvault manages the vault containing user records on disk.
|
||||
// Package passvault manages the vault containing user records on
|
||||
// disk. It contains usernames and associated passwords which are
|
||||
// stored hashed (with salt) using scrypt.
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package passvault
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.crypto/scrypt"
|
||||
"crypto/sha1"
|
||||
"crypto/aes"
|
||||
"crypto/rsa"
|
||||
"crypto/rand"
|
||||
"crypto/cipher"
|
||||
mrand "math/rand"
|
||||
"math/big"
|
||||
"io/ioutil"
|
||||
"encoding/json"
|
||||
"bytes"
|
||||
"code.google.com/p/go.crypto/scrypt"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
mrand "math/rand"
|
||||
"os"
|
||||
"redoctober/padding"
|
||||
)
|
||||
|
||||
// Constants for record type.
|
||||
// Constants for record type
|
||||
const (
|
||||
AESRecord = "AES"
|
||||
RSARecord = "RSA"
|
||||
ECCRecord = "ECC"
|
||||
)
|
||||
|
||||
// Constants for scrypt.
|
||||
// Constants for scrypt
|
||||
const (
|
||||
KEYLENGTH = 16
|
||||
N = 16384
|
||||
R = 8
|
||||
P = 1
|
||||
KEYLENGTH = 16 // 16-byte output from scrypt
|
||||
N = 16384 // Cost parameter
|
||||
R = 8 // Block size
|
||||
P = 1 // Parallelization factor
|
||||
|
||||
DEFAULT_VERSION = 1
|
||||
)
|
||||
|
||||
|
||||
// Set of encrypted records from disk
|
||||
var records diskRecords
|
||||
// Path of current vault
|
||||
var localPath string
|
||||
|
||||
// DiskPasswordRecord is the set of password records on disk.
|
||||
type DiskPasswordRecord struct {
|
||||
Type string
|
||||
Salt []byte
|
||||
// PasswordRecord is the structure used to store password and key
|
||||
// material for a single user name. It is written and read from
|
||||
// storage in JSON format.
|
||||
type PasswordRecord struct {
|
||||
Type string
|
||||
PasswordSalt []byte
|
||||
HashedPassword []byte
|
||||
KeySalt []byte
|
||||
AESKey []byte
|
||||
RSAKey struct {
|
||||
RSAExp []byte
|
||||
RSAExpIV []byte
|
||||
RSAPrimeP []byte
|
||||
KeySalt []byte
|
||||
AESKey []byte
|
||||
RSAKey struct {
|
||||
RSAExp []byte
|
||||
RSAExpIV []byte
|
||||
RSAPrimeP []byte
|
||||
RSAPrimePIV []byte
|
||||
RSAPrimeQ []byte
|
||||
RSAPrimeQ []byte
|
||||
RSAPrimeQIV []byte
|
||||
RSAPublic rsa.PublicKey
|
||||
RSAPublic rsa.PublicKey
|
||||
}
|
||||
Admin bool
|
||||
}
|
||||
|
||||
// diskRecords is the structure used to read and write a JSON file
|
||||
// containing the contents of a password vault
|
||||
type diskRecords struct {
|
||||
Version int
|
||||
VaultId int
|
||||
HmacKey []byte
|
||||
Passwords map[string]DiskPasswordRecord
|
||||
Version int
|
||||
VaultId int
|
||||
HmacKey []byte
|
||||
Passwords map[string]PasswordRecord
|
||||
}
|
||||
|
||||
// Summary is a minmal account summary.
|
||||
// records is the set of encrypted records read from disk and
|
||||
// unmarshalled
|
||||
var records diskRecords
|
||||
|
||||
// Summary is a minmial account summary.
|
||||
type Summary struct {
|
||||
Admin bool
|
||||
Type string
|
||||
Type string
|
||||
}
|
||||
|
||||
// Intialization.
|
||||
func init() {
|
||||
// seed math.random from crypto.random
|
||||
seedBytes, _ := makeRandom(8)
|
||||
seedBuf := bytes.NewBuffer(seedBytes)
|
||||
n64, _ := binary.ReadVarint(seedBuf)
|
||||
mrand.Seed(n64)
|
||||
|
||||
}
|
||||
|
||||
// Take a password and derive a scrypt hashed version
|
||||
func hashPassword(password string, salt []byte) (hashPass []byte, err error) {
|
||||
// hashPassword takes a password and derives a scrypt salted and hashed
|
||||
// version
|
||||
func hashPassword(password string, salt []byte) ([]byte, error) {
|
||||
return scrypt.Key([]byte(password), salt, N, R, P, KEYLENGTH)
|
||||
}
|
||||
|
||||
// Helper to make new buffer full of random data
|
||||
func makeRandom(length int) (bytes []byte, err error) {
|
||||
bytes = make([]byte, 16)
|
||||
n, err := rand.Read(bytes)
|
||||
if n != len(bytes) || err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
// makeRandom is a helper that makes a new buffer full of random data
|
||||
func makeRandom(length int) ([]byte, error) {
|
||||
bytes := make([]byte, length)
|
||||
_, err := rand.Read(bytes)
|
||||
return bytes, err
|
||||
}
|
||||
|
||||
func encryptRSARecord(newRec *DiskPasswordRecord, rsaPriv *rsa.PrivateKey, passKey []byte) (err error) {
|
||||
newRec.RSAKey.RSAExpIV, err = makeRandom(16)
|
||||
if err != nil {
|
||||
func encryptRSARecord(newRec *PasswordRecord, rsaPriv *rsa.PrivateKey, passKey []byte) (err error) {
|
||||
if newRec.RSAKey.RSAExpIV, err = makeRandom(16); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
paddedExponent := padding.PadClearFile(rsaPriv.D.Bytes())
|
||||
newRec.RSAKey.RSAExp, err = encryptCBC(paddedExponent, newRec.RSAKey.RSAExpIV, passKey)
|
||||
if err != nil {
|
||||
paddedExponent := padding.AddPadding(rsaPriv.D.Bytes())
|
||||
if newRec.RSAKey.RSAExp, err = encryptCBC(paddedExponent, newRec.RSAKey.RSAExpIV, passKey); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
newRec.RSAKey.RSAPrimePIV, err = makeRandom(16)
|
||||
if err != nil {
|
||||
if newRec.RSAKey.RSAPrimePIV, err = makeRandom(16); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
paddedPrimeP := padding.PadClearFile(rsaPriv.Primes[0].Bytes())
|
||||
newRec.RSAKey.RSAPrimeP, err = encryptCBC(paddedPrimeP, newRec.RSAKey.RSAPrimePIV, passKey)
|
||||
if err != nil {
|
||||
paddedPrimeP := padding.AddPadding(rsaPriv.Primes[0].Bytes())
|
||||
if newRec.RSAKey.RSAPrimeP, err = encryptCBC(paddedPrimeP, newRec.RSAKey.RSAPrimePIV, passKey); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
newRec.RSAKey.RSAPrimeQIV, err = makeRandom(16)
|
||||
if err != nil {
|
||||
if newRec.RSAKey.RSAPrimeQIV, err = makeRandom(16); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
paddedPrimeQ := padding.PadClearFile(rsaPriv.Primes[1].Bytes())
|
||||
paddedPrimeQ := padding.AddPadding(rsaPriv.Primes[1].Bytes())
|
||||
newRec.RSAKey.RSAPrimeQ, err = encryptCBC(paddedPrimeQ, newRec.RSAKey.RSAPrimeQIV, passKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Create new record from username and password
|
||||
func createPasswordRec(password string, admin bool) (newRec DiskPasswordRecord, err error) {
|
||||
func createPasswordRec(password string, admin bool) (newRec PasswordRecord, err error) {
|
||||
newRec.Type = RSARecord
|
||||
|
||||
newRec.Salt, err = makeRandom(16)
|
||||
if err != nil {
|
||||
if newRec.PasswordSalt, err = makeRandom(16); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
newRec.HashedPassword, err = hashPassword(password, newRec.Salt)
|
||||
if err != nil {
|
||||
if newRec.HashedPassword, err = hashPassword(password, newRec.PasswordSalt); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
newRec.KeySalt, err = makeRandom(16)
|
||||
if err != nil {
|
||||
if newRec.KeySalt, err = makeRandom(16); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -164,8 +161,7 @@ func createPasswordRec(password string, admin bool) (newRec DiskPasswordRecord,
|
||||
}
|
||||
|
||||
// encrypt RSA key with password key
|
||||
err = encryptRSARecord(&newRec, rsaPriv, passKey)
|
||||
if err != nil {
|
||||
if err = encryptRSARecord(&newRec, rsaPriv, passKey); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -177,8 +173,7 @@ func createPasswordRec(password string, admin bool) (newRec DiskPasswordRecord,
|
||||
return
|
||||
}
|
||||
|
||||
newRec.AESKey, err = encryptECB(aesKey, passKey)
|
||||
if err != nil {
|
||||
if newRec.AESKey, err = encryptECB(aesKey, passKey); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -187,13 +182,15 @@ func createPasswordRec(password string, admin bool) (newRec DiskPasswordRecord,
|
||||
return
|
||||
}
|
||||
|
||||
func derivePasswordKey(password string, keySalt []byte) (passwordKey []byte, err error) {
|
||||
// derivePasswordKey generates a key from a password (and salt) using
|
||||
// scrypt
|
||||
func derivePasswordKey(password string, keySalt []byte) ([]byte, error) {
|
||||
return scrypt.Key([]byte(password), keySalt, N, R, P, KEYLENGTH)
|
||||
}
|
||||
|
||||
// Decrypt bytes using a key in ECB mode.
|
||||
func decryptECB(data []byte, passwordKey []byte) (decryptedData []byte, err error) {
|
||||
aesCrypt, err := aes.NewCipher(passwordKey)
|
||||
// decryptECB decrypts bytes using a key in AES ECB mode.
|
||||
func decryptECB(data, key []byte) (decryptedData []byte, err error) {
|
||||
aesCrypt, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -204,9 +201,9 @@ func decryptECB(data []byte, passwordKey []byte) (decryptedData []byte, err erro
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt bytes using a key in ECB mode.
|
||||
func encryptECB(data []byte, passwordKey []byte) (encryptedData []byte, err error) {
|
||||
aesCrypt, err := aes.NewCipher(passwordKey)
|
||||
// encryptECB encrypts bytes using a key in AES ECB mode.
|
||||
func encryptECB(data, key []byte) (encryptedData []byte, err error) {
|
||||
aesCrypt, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -217,9 +214,9 @@ func encryptECB(data []byte, passwordKey []byte) (encryptedData []byte, err erro
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt using a key and IV.
|
||||
func decryptCBC(data []byte, iv []byte, passwordKey []byte) (decryptedData []byte, err error) {
|
||||
aesCrypt, err := aes.NewCipher(passwordKey)
|
||||
// decryptCBC decrypt bytes using a key and IV with AES in CBC mode.
|
||||
func decryptCBC(data, iv, key []byte) (decryptedData []byte, err error) {
|
||||
aesCrypt, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -232,9 +229,9 @@ func decryptCBC(data []byte, iv []byte, passwordKey []byte) (decryptedData []byt
|
||||
return
|
||||
}
|
||||
|
||||
// Encrypt using a key and IV.
|
||||
func encryptCBC(data []byte, iv []byte, passwordKey []byte) (encryptedData []byte, err error) {
|
||||
aesCrypt, err := aes.NewCipher(passwordKey)
|
||||
// encryptCBC encrypt data using a key and IV with AES in CBC mode.
|
||||
func encryptCBC(data, iv, key []byte) (encryptedData []byte, err error) {
|
||||
aesCrypt, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -248,124 +245,127 @@ func encryptCBC(data []byte, iv []byte, passwordKey []byte) (encryptedData []byt
|
||||
}
|
||||
|
||||
// InitFromDisk reads the record from disk and initialize global context.
|
||||
func InitFromDisk(path string) {
|
||||
func InitFromDisk(path string) error {
|
||||
jsonDiskRecord, err := ioutil.ReadFile(path)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(jsonDiskRecord, &records)
|
||||
|
||||
// It's OK for the file to be missing, we'll create it later if
|
||||
// anything is added.
|
||||
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// validate sizes
|
||||
formatErr := false
|
||||
// Initialized so that we can determine later if anything was read
|
||||
// from the file.
|
||||
|
||||
records.Version = 0
|
||||
|
||||
if len(jsonDiskRecord) != 0 {
|
||||
if err = json.Unmarshal(jsonDiskRecord, &records); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
formatErr := errors.New("Format error")
|
||||
for _, rec := range records.Passwords {
|
||||
if len(rec.Salt) != 16 {
|
||||
formatErr = true
|
||||
if len(rec.PasswordSalt) != 16 {
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.HashedPassword) != 16 {
|
||||
formatErr = true
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.KeySalt) != 16 {
|
||||
formatErr = true
|
||||
return formatErr
|
||||
}
|
||||
if rec.Type == AESRecord {
|
||||
if len(rec.AESKey) != 16 {
|
||||
formatErr = true
|
||||
return formatErr
|
||||
}
|
||||
}
|
||||
if rec.Type == RSARecord {
|
||||
if len(rec.RSAKey.RSAExp) == 0 || len(rec.RSAKey.RSAExp) % 16 != 0 {
|
||||
formatErr = true
|
||||
if len(rec.RSAKey.RSAExp) == 0 || len(rec.RSAKey.RSAExp)%16 != 0 {
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAPrimeP) == 0 || len(rec.RSAKey.RSAPrimeP) % 16 != 0 {
|
||||
formatErr = true
|
||||
if len(rec.RSAKey.RSAPrimeP) == 0 || len(rec.RSAKey.RSAPrimeP)%16 != 0 {
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAPrimeQ) == 0 || len(rec.RSAKey.RSAPrimeQ) % 16 != 0 {
|
||||
formatErr = true
|
||||
if len(rec.RSAKey.RSAPrimeQ) == 0 || len(rec.RSAKey.RSAPrimeQ)%16 != 0 {
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAExpIV) != 16 {
|
||||
formatErr = true
|
||||
if len(rec.RSAKey.RSAExpIV) != 16 {
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAPrimePIV) != 16 {
|
||||
formatErr = true
|
||||
if len(rec.RSAKey.RSAPrimePIV) != 16 {
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAPrimeQIV) != 16 {
|
||||
formatErr = true
|
||||
if len(rec.RSAKey.RSAPrimeQIV) != 16 {
|
||||
return formatErr
|
||||
}
|
||||
}
|
||||
if formatErr {
|
||||
err = errors.New("Format error")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// If the Version field is 0 then it indicates that nothing was
|
||||
// read from the file and so it needs to be initialized.
|
||||
|
||||
if records.Version == 0 {
|
||||
records.Version = DEFAULT_VERSION
|
||||
records.VaultId = mrand.Int()
|
||||
records.HmacKey, err = makeRandom(16)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
// make the record data holder
|
||||
records.Passwords = make(map[string]DiskPasswordRecord)
|
||||
records.Passwords = make(map[string]PasswordRecord)
|
||||
}
|
||||
|
||||
localPath = path
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRecordsToDisk saves the current state of the records to disk.
|
||||
func WriteRecordsToDisk() (err error) {
|
||||
func WriteRecordsToDisk() error {
|
||||
if !IsInitialized() {
|
||||
err = errors.New("Path not initialized")
|
||||
return
|
||||
return errors.New("Path not initialized")
|
||||
}
|
||||
|
||||
jsonDiskRecord, err := json.Marshal(records)
|
||||
if err == nil {
|
||||
err = ioutil.WriteFile(localPath, jsonDiskRecord, 0644)
|
||||
if jsonDiskRecord, err := json.Marshal(records); err == nil {
|
||||
return ioutil.WriteFile(localPath, jsonDiskRecord, 0644)
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AddNewRecord adds a new record for a given username and password.
|
||||
func AddNewRecord(name string, password string, admin bool) (passwordRec DiskPasswordRecord, err error) {
|
||||
passwordRec, err = createPasswordRec(password, admin)
|
||||
if err != nil {
|
||||
return
|
||||
func AddNewRecord(name, password string, admin bool) (PasswordRecord, error) {
|
||||
if pr, err := createPasswordRec(password, admin); err == nil {
|
||||
SetRecord(pr, name)
|
||||
return pr, WriteRecordsToDisk()
|
||||
} else {
|
||||
return pr, err
|
||||
}
|
||||
|
||||
SetRecord(passwordRec, name)
|
||||
|
||||
err = WriteRecordsToDisk()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ChangePassword changes the password for a given user.
|
||||
func ChangePassword(name string, password string, newPassword string) (err error) {
|
||||
// find and validate name and password
|
||||
passwordRec, ok := GetRecord(name)
|
||||
func ChangePassword(name, password, newPassword string) (err error) {
|
||||
pr, ok := GetRecord(name)
|
||||
if !ok {
|
||||
err = errors.New("Record not present")
|
||||
return
|
||||
}
|
||||
err = passwordRec.ValidatePassword(password)
|
||||
if err != nil {
|
||||
if err = pr.ValidatePassword(password); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// decrypt key
|
||||
var key []byte
|
||||
var rsaKey rsa.PrivateKey
|
||||
if passwordRec.Type == AESRecord {
|
||||
key, err = passwordRec.GetKeyAES(password)
|
||||
if pr.Type == AESRecord {
|
||||
key, err = pr.GetKeyAES(password)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if passwordRec.Type == RSARecord {
|
||||
rsaKey, err = passwordRec.GetKeyRSA(password)
|
||||
} else if pr.Type == RSARecord {
|
||||
rsaKey, err = pr.GetKeyRSA(password)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -374,38 +374,30 @@ func ChangePassword(name string, password string, newPassword string) (err error
|
||||
return
|
||||
}
|
||||
|
||||
// create new salt
|
||||
passwordRec.Salt, err = makeRandom(16)
|
||||
if err != nil {
|
||||
if pr.PasswordSalt, err = makeRandom(16); err != nil {
|
||||
return
|
||||
}
|
||||
if pr.HashedPassword, err = hashPassword(newPassword, pr.PasswordSalt); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// hash new password
|
||||
passwordRec.HashedPassword, err = hashPassword(newPassword, passwordRec.Salt)
|
||||
if err != nil {
|
||||
if pr.KeySalt, err = makeRandom(16); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// create new key salt
|
||||
passwordRec.KeySalt, err = makeRandom(16)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
newPassKey, err := derivePasswordKey(newPassword, passwordRec.KeySalt)
|
||||
newPassKey, err := derivePasswordKey(newPassword, pr.KeySalt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// encrypt original key with new password
|
||||
if passwordRec.Type == AESRecord {
|
||||
passwordRec.AESKey, err = encryptECB(key, newPassKey)
|
||||
if pr.Type == AESRecord {
|
||||
pr.AESKey, err = encryptECB(key, newPassKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if passwordRec.Type == RSARecord {
|
||||
// encrypt RSA key with password key
|
||||
err = encryptRSARecord(&passwordRec, &rsaKey, newPassKey)
|
||||
} else if pr.Type == RSARecord {
|
||||
// encrypt RSA key with password key
|
||||
err = encryptRSARecord(&pr, &rsaKey, newPassKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -414,60 +406,52 @@ func ChangePassword(name string, password string, newPassword string) (err error
|
||||
return
|
||||
}
|
||||
|
||||
SetRecord(passwordRec, name)
|
||||
SetRecord(pr, name)
|
||||
|
||||
// update disk record
|
||||
err = WriteRecordsToDisk()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
return WriteRecordsToDisk()
|
||||
}
|
||||
|
||||
// DeleteRecord deletes a given record.
|
||||
func DeleteRecord(name string) error {
|
||||
if _, ok := GetRecord(name); ok {
|
||||
delete(records.Passwords, name)
|
||||
} else {
|
||||
return errors.New("Record missing")
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
return errors.New("Record missing")
|
||||
}
|
||||
|
||||
// RevokeRecord removes admin status from a record.
|
||||
func RevokeRecord(name string) error {
|
||||
rec, ok := GetRecord(name)
|
||||
if ok {
|
||||
if rec, ok := GetRecord(name); ok {
|
||||
rec.Admin = false
|
||||
SetRecord(rec, name)
|
||||
} else {
|
||||
return errors.New("Record missing")
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
return errors.New("Record missing")
|
||||
}
|
||||
|
||||
// MakeAdmin adds admin status to a given record.
|
||||
func MakeAdmin(name string) error {
|
||||
rec, ok := GetRecord(name)
|
||||
if ok {
|
||||
if rec, ok := GetRecord(name); ok {
|
||||
rec.Admin = true
|
||||
SetRecord(rec, name)
|
||||
} else {
|
||||
return errors.New("Record missing")
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
return errors.New("Record missing")
|
||||
}
|
||||
|
||||
// SetRecord puts a record into the global status.
|
||||
func SetRecord(passwordRec DiskPasswordRecord, name string) {
|
||||
records.Passwords[name] = passwordRec
|
||||
func SetRecord(pr PasswordRecord, name string) {
|
||||
records.Passwords[name] = pr
|
||||
}
|
||||
|
||||
// GetRecord returns a record given a name.
|
||||
func GetRecord(name string) (passwordRec DiskPasswordRecord, ok bool) {
|
||||
passwordRec, ok = records.Passwords[name]
|
||||
return
|
||||
func GetRecord(name string) (PasswordRecord, bool) {
|
||||
dpr, found := records.Passwords[name]
|
||||
return dpr, found
|
||||
}
|
||||
|
||||
// GetVaultId returns the id of the current vault.
|
||||
@@ -502,71 +486,70 @@ func NumRecords() int {
|
||||
func GetSummary() (summary map[string]Summary) {
|
||||
summary = make(map[string]Summary)
|
||||
for name, pass := range records.Passwords {
|
||||
tempName := Summary{pass.Admin, pass.Type}
|
||||
summary[name] = tempName
|
||||
summary[name] = Summary{pass.Admin, pass.Type}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// IsAdmin returns the admin status of the DiskPasswordRecord.
|
||||
func (passwordRec DiskPasswordRecord) IsAdmin() bool {
|
||||
return passwordRec.Admin
|
||||
// IsAdmin returns the admin status of the PasswordRecord.
|
||||
func (pr PasswordRecord) IsAdmin() bool {
|
||||
return pr.Admin
|
||||
}
|
||||
|
||||
// GetType returns the type status of the DiskPasswordRecord.
|
||||
func (passwordRec DiskPasswordRecord) GetType() string {
|
||||
return passwordRec.Type
|
||||
// GetType returns the type status of the PasswordRecord.
|
||||
func (pr PasswordRecord) GetType() string {
|
||||
return pr.Type
|
||||
}
|
||||
|
||||
// EncryptKey encrypts a 16-byte key with the RSA key of the record.
|
||||
func (passwordRec DiskPasswordRecord) EncryptKey(in []byte) (out []byte, err error) {
|
||||
return rsa.EncryptOAEP(sha1.New(), rand.Reader, &passwordRec.RSAKey.RSAPublic, in, nil)
|
||||
func (pr PasswordRecord) EncryptKey(in []byte) (out []byte, err error) {
|
||||
return rsa.EncryptOAEP(sha1.New(), rand.Reader, &pr.RSAKey.RSAPublic, in, nil)
|
||||
}
|
||||
|
||||
// GetKeyAES returns the 16-byte key of the record.
|
||||
func (passwordRec DiskPasswordRecord) GetKeyAES(password string) (key []byte, err error) {
|
||||
if passwordRec.Type != AESRecord {
|
||||
func (pr PasswordRecord) GetKeyAES(password string) (key []byte, err error) {
|
||||
if pr.Type != AESRecord {
|
||||
return nil, errors.New("Invalid function for record type")
|
||||
}
|
||||
|
||||
err = passwordRec.ValidatePassword(password)
|
||||
err = pr.ValidatePassword(password)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
passKey, err := derivePasswordKey(password, passwordRec.KeySalt)
|
||||
passKey, err := derivePasswordKey(password, pr.KeySalt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return decryptECB(passwordRec.AESKey, passKey)
|
||||
return decryptECB(pr.AESKey, passKey)
|
||||
}
|
||||
|
||||
// GetKeyAES returns the RSA public key of the record.
|
||||
func (passwordRec DiskPasswordRecord) GetKeyRSAPub() (out *rsa.PublicKey, err error) {
|
||||
if passwordRec.Type != RSARecord {
|
||||
func (pr PasswordRecord) GetKeyRSAPub() (out *rsa.PublicKey, err error) {
|
||||
if pr.Type != RSARecord {
|
||||
return out, errors.New("Invalid function for record type")
|
||||
}
|
||||
return &passwordRec.RSAKey.RSAPublic, err
|
||||
return &pr.RSAKey.RSAPublic, err
|
||||
}
|
||||
|
||||
// GetKeyAES returns the RSA private key of the record given the correct password.
|
||||
func (passwordRec DiskPasswordRecord) GetKeyRSA(password string) (key rsa.PrivateKey, err error) {
|
||||
if passwordRec.Type != RSARecord {
|
||||
func (pr PasswordRecord) GetKeyRSA(password string) (key rsa.PrivateKey, err error) {
|
||||
if pr.Type != RSARecord {
|
||||
return key, errors.New("Invalid function for record type")
|
||||
}
|
||||
|
||||
err = passwordRec.ValidatePassword(password)
|
||||
err = pr.ValidatePassword(password)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
passKey, err := derivePasswordKey(password, passwordRec.KeySalt)
|
||||
passKey, err := derivePasswordKey(password, pr.KeySalt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rsaExponentPadded, err := decryptCBC(passwordRec.RSAKey.RSAExp, passwordRec.RSAKey.RSAExpIV, passKey)
|
||||
rsaExponentPadded, err := decryptCBC(pr.RSAKey.RSAExp, pr.RSAKey.RSAExpIV, passKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -575,7 +558,7 @@ func (passwordRec DiskPasswordRecord) GetKeyRSA(password string) (key rsa.Privat
|
||||
return
|
||||
}
|
||||
|
||||
rsaPrimePPadded, err := decryptCBC(passwordRec.RSAKey.RSAPrimeP, passwordRec.RSAKey.RSAPrimePIV, passKey)
|
||||
rsaPrimePPadded, err := decryptCBC(pr.RSAKey.RSAPrimeP, pr.RSAKey.RSAPrimePIV, passKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -584,7 +567,7 @@ func (passwordRec DiskPasswordRecord) GetKeyRSA(password string) (key rsa.Privat
|
||||
return
|
||||
}
|
||||
|
||||
rsaPrimeQPadded, err := decryptCBC(passwordRec.RSAKey.RSAPrimeQ, passwordRec.RSAKey.RSAPrimeQIV, passKey)
|
||||
rsaPrimeQPadded, err := decryptCBC(pr.RSAKey.RSAPrimeQ, pr.RSAKey.RSAPrimeQIV, passKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -593,7 +576,7 @@ func (passwordRec DiskPasswordRecord) GetKeyRSA(password string) (key rsa.Privat
|
||||
return
|
||||
}
|
||||
|
||||
key.PublicKey = passwordRec.RSAKey.RSAPublic
|
||||
key.PublicKey = pr.RSAKey.RSAPublic
|
||||
key.D = big.NewInt(0).SetBytes(rsaExponent)
|
||||
key.Primes = []*big.Int{big.NewInt(0), big.NewInt(0)}
|
||||
key.Primes[0].SetBytes(rsaPrimeP)
|
||||
@@ -608,15 +591,14 @@ func (passwordRec DiskPasswordRecord) GetKeyRSA(password string) (key rsa.Privat
|
||||
}
|
||||
|
||||
// ValidatePassword returns an error if the password is incorrect.
|
||||
func (passwordRec DiskPasswordRecord) ValidatePassword(password string) (err error) {
|
||||
sha, err := hashPassword(password, passwordRec.Salt)
|
||||
if err != nil {
|
||||
return
|
||||
func (pr PasswordRecord) ValidatePassword(password string) error {
|
||||
if h, err := hashPassword(password, pr.PasswordSalt); err != nil {
|
||||
return err
|
||||
} else {
|
||||
if bytes.Compare(h, pr.HashedPassword) != 0 {
|
||||
return errors.New("Wrong Password")
|
||||
}
|
||||
}
|
||||
|
||||
if bytes.Compare(sha, passwordRec.HashedPassword) != 0 {
|
||||
return errors.New("Wrong Password")
|
||||
}
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// passvault_test: tests for passvault.go
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package passvault
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,84 +1,76 @@
|
||||
// Package redoctober contains the server code for Red October.
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"flag"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"crypto/tls"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"redoctober/core"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// list of URLs to register
|
||||
const (
|
||||
Create string = "/create"
|
||||
Summary = "/summary"
|
||||
Delegate = "/delegate"
|
||||
Password = "/password"
|
||||
Encrypt = "/encrypt"
|
||||
Decrypt = "/decrypt"
|
||||
Modify = "/modify"
|
||||
)
|
||||
// List of URLs to register and their related functions
|
||||
|
||||
// the channel handling user request
|
||||
var process = make(chan userRequest)
|
||||
var functions = map[string]func([]byte) ([]byte, error){
|
||||
"/create": core.Create,
|
||||
"/summary": core.Summary,
|
||||
"/delegate": core.Delegate,
|
||||
"/password": core.Password,
|
||||
"/encrypt": core.Encrypt,
|
||||
"/decrypt": core.Decrypt,
|
||||
"/modify": core.Modify,
|
||||
}
|
||||
|
||||
type userRequest struct {
|
||||
rt string
|
||||
in []byte
|
||||
resp chan []byte
|
||||
rt string // The request type (which will be one of the
|
||||
// keys of the functions map above
|
||||
in []byte // Arbitrary input data (depends on the core.*
|
||||
// function called)
|
||||
resp chan []byte // Channel down which a response is sent (the
|
||||
// data sent will depend on the core.* function
|
||||
// called to handle this request)
|
||||
}
|
||||
|
||||
func init() {
|
||||
go func () {
|
||||
for {
|
||||
foo := <-process
|
||||
switch {
|
||||
case foo.rt == Create:
|
||||
foo.resp <- core.Create(foo.in)
|
||||
case foo.rt == Summary:
|
||||
foo.resp <- core.Summary(foo.in)
|
||||
case foo.rt == Delegate:
|
||||
foo.resp <- core.Delegate(foo.in)
|
||||
case foo.rt == Password:
|
||||
foo.resp <- core.Password(foo.in)
|
||||
case foo.rt == Encrypt:
|
||||
foo.resp <- core.Encrypt(foo.in)
|
||||
case foo.rt == Decrypt:
|
||||
foo.resp <- core.Decrypt(foo.in)
|
||||
case foo.rt == Modify:
|
||||
foo.resp <- core.Modify(foo.in)
|
||||
default:
|
||||
fmt.Printf("Unknown! %s\n", foo.rt)
|
||||
foo.resp <- []byte("Unknown command")
|
||||
}
|
||||
}
|
||||
} ()
|
||||
}
|
||||
|
||||
func queueRequest(requestType string, w http.ResponseWriter, r *http.Request, c *tls.ConnectionState) {
|
||||
// queueRequest handles a single request receive on the JSON API for
|
||||
// one of the functions named in the functions map above. It reads the
|
||||
// request and sends it to the goroutine started in main() below for
|
||||
// processing and then waits for the response.
|
||||
func queueRequest(process chan userRequest, requestType string, w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response := make(chan []byte, 1)
|
||||
req := userRequest{rt: requestType, in: body, resp: response}
|
||||
process <-req
|
||||
response := make(chan []byte)
|
||||
process <- userRequest{rt: requestType, in: body, resp: response}
|
||||
|
||||
code := <-response
|
||||
|
||||
w.Write(code)
|
||||
if resp, ok := <-response; ok {
|
||||
w.Write(resp)
|
||||
} else {
|
||||
http.Error(w, "Unknown request", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func NewServer(addr string, certPath string, keyPath string, caPath string) (*http.Server, *net.Listener, error) {
|
||||
// set up server
|
||||
// NewServer starts an HTTPS server the handles the redoctober JSON
|
||||
// API. Each of the URIs in the functions map above is setup with a
|
||||
// separate HandleFunc. Each HandleFunc is an instance of queueRequest
|
||||
// above.
|
||||
//
|
||||
// Returns a valid http.Server handling redoctober JSON requests (and
|
||||
// its associated listener) or an error
|
||||
func NewServer(process chan userRequest, addr string, certPath, keyPath, caPath string) (*http.Server, *net.Listener, error) {
|
||||
mux := http.NewServeMux()
|
||||
srv := http.Server{
|
||||
Addr: addr,
|
||||
@@ -86,35 +78,33 @@ func NewServer(addr string, certPath string, keyPath string, caPath string) (*ht
|
||||
}
|
||||
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("Error loading certificate (%s, %s): %s", certPath, keyPath, err)
|
||||
}
|
||||
|
||||
config := tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
Rand: rand.Reader,
|
||||
ClientAuth: tls.RequestClientCert,
|
||||
Certificates: []tls.Certificate{cert},
|
||||
Rand: rand.Reader,
|
||||
ClientAuth: tls.RequestClientCert,
|
||||
PreferServerCipherSuites: true,
|
||||
SessionTicketsDisabled: true,
|
||||
SessionTicketsDisabled: true,
|
||||
}
|
||||
config.Rand = rand.Reader
|
||||
|
||||
// create local cert pool if present
|
||||
// If a caPath has been specified then a local CA is being used
|
||||
// and not the system configuration.
|
||||
|
||||
if caPath != "" {
|
||||
rootPool := x509.NewCertPool()
|
||||
pemCert, err := ioutil.ReadFile(caPath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("Error reading %s: %s\n", caPath, err)
|
||||
}
|
||||
derCert, pemCert := pem.Decode(pemCert)
|
||||
if derCert == nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("Error decoding CA certificate: %s\n", err)
|
||||
}
|
||||
cert, err := x509.ParseCertificate(derCert.Bytes)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("Error parsing CA certificate: %s\n", err)
|
||||
}
|
||||
|
||||
rootPool.AddCert(cert)
|
||||
@@ -123,16 +113,14 @@ func NewServer(addr string, certPath string, keyPath string, caPath string) (*ht
|
||||
|
||||
conn, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, nil, err
|
||||
return nil, nil, fmt.Errorf("Error starting TCP listener on %s: %s\n", addr, err)
|
||||
}
|
||||
|
||||
lstnr := tls.NewListener(conn, &config)
|
||||
|
||||
for _, action := range []string {Create, Summary, Delegate, Password, Encrypt, Decrypt, Modify} {
|
||||
var requestType = action
|
||||
for requestType := range functions {
|
||||
mux.HandleFunc(requestType, func(w http.ResponseWriter, r *http.Request) {
|
||||
queueRequest(requestType, w, r, r.TLS)
|
||||
queueRequest(process, requestType, w, r)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -144,11 +132,10 @@ const usage = `Usage:
|
||||
redoctober -vaultpath <path> -addr <addr> -cert <path> -key <path> [-ca <path>]
|
||||
|
||||
example:
|
||||
redoctober /tmp/diskrecord.json localhost:8080 cert.pem cert.key
|
||||
|
||||
redoctober -vaultpath /tmp/diskrecord.json -addr localhost:8080 -cert cert.pem -key cert.key
|
||||
`
|
||||
|
||||
func main () {
|
||||
func main() {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprint(os.Stderr, usage)
|
||||
flag.PrintDefaults()
|
||||
@@ -168,8 +155,44 @@ func main () {
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
core.Init(*vaultPath)
|
||||
s, l, _ := NewServer(*addr, *certPath, *keyPath, *caPath)
|
||||
s.Serve(*l)
|
||||
}
|
||||
if err := core.Init(*vaultPath); err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
// The core package is not safe to be shared across goroutines so
|
||||
// this supervisor goroutine reads requests from the process
|
||||
// channel and dispatches them to core for processes.
|
||||
|
||||
process := make(chan userRequest)
|
||||
go func() {
|
||||
for {
|
||||
req := <-process
|
||||
if f, ok := functions[req.rt]; ok {
|
||||
r, err := f(req.in)
|
||||
if err == nil {
|
||||
req.resp <- r
|
||||
} else {
|
||||
log.Printf("Error handling %s: %s\n", req.rt, err)
|
||||
}
|
||||
} else {
|
||||
log.Printf("Unknown user request received: %s\n", req.rt)
|
||||
}
|
||||
|
||||
// Note that if an error occurs no message is sent down
|
||||
// the channel and then channel is closed. The
|
||||
// queueRequest function will see this as indication of an
|
||||
// error.
|
||||
|
||||
close(req.resp)
|
||||
}
|
||||
}()
|
||||
|
||||
s, l, err := NewServer(process, *addr, *certPath, *keyPath, *caPath)
|
||||
if err == nil {
|
||||
s.Serve(*l)
|
||||
} else {
|
||||
log.Fatalf("Error starting redoctober server: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user