mirror of
https://github.com/cloudflare/redoctober.git
synced 2026-01-10 07:58:03 +00:00
First time a second set of eyes has looked at the code
The major work here has been: 1. Clean up some go idioms to make the code more idiomatic and simplify functions where possible to minimize number of lines where there was unnecessary code. 2. Document as many of the functions and packages as possible. 3. Add a test suite for the padding package. 4. Force 'go fmt' when 'make' is performed. 5. Better handling of errors throughout the code. There were quite a few that were ignored, now they are handled and reported on (e.g. if the JSON in the password vault is broken it will no longer be ignored, etc.) 6. Changed the names of a number of functions to make them clearer. The code could now do with someone who knows it well to go through and document everything clearly for release.
This commit is contained in:
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 $*=$($*)
|
||||
|
||||
@@ -7,56 +7,69 @@ package core
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"redoctober/cryptor"
|
||||
"redoctober/keycache"
|
||||
"redoctober/passvault"
|
||||
)
|
||||
|
||||
type credential struct {
|
||||
// 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
|
||||
Password string
|
||||
}
|
||||
|
||||
// format of incoming sign-in request
|
||||
type create struct {
|
||||
credential
|
||||
}
|
||||
|
||||
type summary struct {
|
||||
credential
|
||||
Name string
|
||||
Password string
|
||||
}
|
||||
|
||||
type delegate struct {
|
||||
credential
|
||||
Name string
|
||||
Password string
|
||||
|
||||
Uses int
|
||||
Time string
|
||||
}
|
||||
|
||||
type password struct {
|
||||
credential
|
||||
Name string
|
||||
Password string
|
||||
|
||||
NewPassword string
|
||||
}
|
||||
|
||||
type encrypt struct {
|
||||
credential
|
||||
Name string
|
||||
Password string
|
||||
|
||||
Minimum int
|
||||
Owners []string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type decrypt struct {
|
||||
credential
|
||||
Name string
|
||||
Password string
|
||||
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type modify struct {
|
||||
credential
|
||||
Name string
|
||||
Password string
|
||||
|
||||
ToModify string
|
||||
Command string
|
||||
}
|
||||
|
||||
// response JSON format
|
||||
// These structures map the JSON responses that will be sent from the API
|
||||
|
||||
type status struct {
|
||||
Status string
|
||||
}
|
||||
@@ -72,250 +85,226 @@ type summaryData struct {
|
||||
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"))
|
||||
}
|
||||
|
||||
var err error
|
||||
switch s.Command {
|
||||
case "delete":
|
||||
{
|
||||
err = passvault.DeleteRecord(s.ToModify)
|
||||
}
|
||||
err = passvault.DeleteRecord(s.ToModify)
|
||||
case "revoke":
|
||||
{
|
||||
err = passvault.RevokeRecord(s.ToModify)
|
||||
}
|
||||
err = passvault.RevokeRecord(s.ToModify)
|
||||
case "admin":
|
||||
{
|
||||
err = passvault.MakeAdmin(s.ToModify)
|
||||
}
|
||||
err = passvault.MakeAdmin(s.ToModify)
|
||||
default:
|
||||
{
|
||||
return errToJson(errors.New("Unknown command"))
|
||||
}
|
||||
return jsonStatusError(errors.New("Unknown command"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return jsonStatusError(err)
|
||||
} else {
|
||||
return jsonStatusOk()
|
||||
}
|
||||
return errToJson(err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// core_test.go: tests for core.go
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
@@ -8,7 +9,7 @@ import (
|
||||
"os"
|
||||
"redoctober/keycache"
|
||||
"redoctober/passvault"
|
||||
"redoctober/testing"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
@@ -17,10 +18,13 @@ func TestCreate(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
@@ -29,7 +33,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)
|
||||
@@ -47,9 +54,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)
|
||||
}
|
||||
@@ -60,7 +70,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)
|
||||
@@ -69,7 +82,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)
|
||||
@@ -90,7 +106,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)
|
||||
@@ -99,7 +118,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)
|
||||
@@ -141,7 +163,6 @@ func TestSummary(t *testing.T) {
|
||||
t.Fatalf("Error in summary of account, record missing ")
|
||||
}
|
||||
|
||||
//
|
||||
keycache.FlushCache()
|
||||
|
||||
os.Remove("/tmp/db1.json")
|
||||
@@ -159,8 +180,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)
|
||||
}
|
||||
@@ -168,7 +192,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)
|
||||
@@ -177,7 +204,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)
|
||||
@@ -186,7 +216,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)
|
||||
@@ -195,7 +228,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)
|
||||
@@ -204,7 +240,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)
|
||||
@@ -213,7 +252,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)
|
||||
@@ -222,7 +264,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)
|
||||
@@ -251,16 +296,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)
|
||||
@@ -269,7 +320,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)
|
||||
@@ -280,7 +334,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 {
|
||||
@@ -294,7 +351,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)
|
||||
@@ -304,7 +364,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)
|
||||
@@ -314,12 +377,15 @@ func TestEncryptDecrypt(t *testing.T) {
|
||||
}
|
||||
|
||||
// decrypt file
|
||||
decryptJson, err := json.Marshal(decrypt{Name: "Alice", Password: "Hello", In: s.Response})
|
||||
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)
|
||||
@@ -329,7 +395,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)
|
||||
@@ -338,7 +407,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)
|
||||
@@ -350,7 +422,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)
|
||||
@@ -362,7 +437,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)
|
||||
@@ -396,16 +474,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)
|
||||
@@ -414,7 +498,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)
|
||||
@@ -425,7 +512,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 {
|
||||
@@ -439,7 +529,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)
|
||||
@@ -449,7 +542,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)
|
||||
@@ -459,7 +555,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)
|
||||
@@ -468,7 +567,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)
|
||||
@@ -481,7 +583,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)
|
||||
@@ -490,7 +595,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)
|
||||
@@ -503,7 +611,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)
|
||||
@@ -513,7 +624,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)
|
||||
|
||||
@@ -22,23 +22,23 @@ 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
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
@@ -60,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}
|
||||
|
||||
@@ -109,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
|
||||
}
|
||||
|
||||
@@ -161,17 +160,17 @@ func unwrapKey(keys []MultiWrappedKey, rsaKeys map[string]SingleWrappedKey) (unw
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -260,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 {
|
||||
@@ -299,8 +297,7 @@ 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
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ type ActiveUser struct {
|
||||
Type string
|
||||
Expiry time.Time
|
||||
Uses int
|
||||
// non-public members
|
||||
|
||||
aesKey []byte
|
||||
rsaKey rsa.PrivateKey
|
||||
}
|
||||
@@ -41,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)
|
||||
}
|
||||
@@ -71,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()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
package keycache
|
||||
|
||||
import (
|
||||
"passvault"
|
||||
"redoctober/passvault"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -6,25 +6,41 @@ 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,5 +1,6 @@
|
||||
// Package passvault manages the vault containing user records on
|
||||
// disk.
|
||||
// disk. It contains usernames and associated passwords which are
|
||||
// stored hashed (with salt) using scrypt.
|
||||
//
|
||||
// Copyright (c) 2013 CloudFlare, Inc.
|
||||
|
||||
@@ -19,36 +20,36 @@ import (
|
||||
"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 {
|
||||
// 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
|
||||
Salt []byte
|
||||
PasswordSalt []byte
|
||||
HashedPassword []byte
|
||||
KeySalt []byte
|
||||
AESKey []byte
|
||||
@@ -63,96 +64,88 @@ type DiskPasswordRecord struct {
|
||||
}
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -168,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
|
||||
}
|
||||
|
||||
@@ -181,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
|
||||
}
|
||||
|
||||
@@ -191,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
|
||||
}
|
||||
@@ -208,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
|
||||
}
|
||||
@@ -221,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
|
||||
}
|
||||
@@ -236,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
|
||||
}
|
||||
@@ -252,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
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAPrimeP) == 0 || len(rec.RSAKey.RSAPrimeP)%16 != 0 {
|
||||
formatErr = true
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAPrimeQ) == 0 || len(rec.RSAKey.RSAPrimeQ)%16 != 0 {
|
||||
formatErr = true
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAExpIV) != 16 {
|
||||
formatErr = true
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAPrimePIV) != 16 {
|
||||
formatErr = true
|
||||
return formatErr
|
||||
}
|
||||
if len(rec.RSAKey.RSAPrimeQIV) != 16 {
|
||||
formatErr = true
|
||||
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
|
||||
}
|
||||
@@ -378,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 {
|
||||
} else if pr.Type == RSARecord {
|
||||
// encrypt RSA key with password key
|
||||
err = encryptRSARecord(&passwordRec, &rsaKey, newPassKey)
|
||||
err = encryptRSARecord(&pr, &rsaKey, newPassKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -418,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.
|
||||
@@ -506,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
|
||||
}
|
||||
@@ -579,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
|
||||
}
|
||||
@@ -588,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
|
||||
}
|
||||
@@ -597,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)
|
||||
@@ -612,14 +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
|
||||
}
|
||||
|
||||
@@ -12,77 +12,65 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"redoctober/core"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// List of URLs to register
|
||||
// List of URLs to register and their related functions
|
||||
|
||||
const (
|
||||
Create string = "/create"
|
||||
Summary = "/summary"
|
||||
Delegate = "/delegate"
|
||||
Password = "/password"
|
||||
Encrypt = "/encrypt"
|
||||
Decrypt = "/decrypt"
|
||||
Modify = "/modify"
|
||||
)
|
||||
|
||||
// The channel handling user requests
|
||||
|
||||
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 {
|
||||
req := <-process
|
||||
switch {
|
||||
case req.rt == Create:
|
||||
req.resp <- core.Create(req.in)
|
||||
case req.rt == Summary:
|
||||
req.resp <- core.Summary(req.in)
|
||||
case req.rt == Delegate:
|
||||
req.resp <- core.Delegate(req.in)
|
||||
case req.rt == Password:
|
||||
req.resp <- core.Password(req.in)
|
||||
case req.rt == Encrypt:
|
||||
req.resp <- core.Encrypt(req.in)
|
||||
case req.rt == Decrypt:
|
||||
req.resp <- core.Decrypt(req.in)
|
||||
case req.rt == Modify:
|
||||
req.resp <- core.Modify(req.in)
|
||||
default:
|
||||
fmt.Printf("Unknown! %s\n", req.rt)
|
||||
req.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) {
|
||||
// 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,
|
||||
@@ -90,8 +78,7 @@ 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{
|
||||
@@ -101,24 +88,23 @@ func NewServer(addr string, certPath string, keyPath string, caPath string) (*ht
|
||||
PreferServerCipherSuites: 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)
|
||||
@@ -127,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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -148,8 +132,7 @@ 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() {
|
||||
@@ -172,7 +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