Add SSH wrapper to RO client

Add Usages field to EncryptedData to allow creation of a file which
can only be used to create signatures and cannot be directly decrypted
This commit is contained in:
Andrew Buss
2015-12-03 06:16:14 -08:00
committed by Kyle Isom
parent a45f80f613
commit 9035eed8fd
4 changed files with 42 additions and 35 deletions

View File

@@ -254,7 +254,7 @@ func (c *RemoteServer) Decrypt(req core.DecryptRequest) (*core.ResponseData, err
}
// Decrypt issues an decrypt request to the remote server
// DecryptSign issues an decrypt-sign request to the remote server
func (c *RemoteServer) DecryptSign(req core.DecryptSignRequest) (*core.ResponseData, error) {
reqBytes, err := json.Marshal(req)
if err != nil {

View File

@@ -1,3 +1,5 @@
// Package roagent provides ROAgent, which implements the SSH agent interface,
// forwarding sign requests to a Red October server
package roagent
import (
@@ -5,7 +7,6 @@ import (
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"io"
"log"
@@ -43,7 +44,6 @@ func (signer ROSigner) Sign(rand io.Reader, msg []byte) (signature *ssh.Signatur
log.Fatal("response status error:", resp.Status)
return nil, errors.New("response status error")
}
fmt.Println("Response Status:", resp.Status)
var respMsg core.DecryptSignWithDelegates
err = json.Unmarshal(resp.Response, &respMsg)
@@ -51,12 +51,7 @@ func (signer ROSigner) Sign(rand io.Reader, msg []byte) (signature *ssh.Signatur
return nil, err
}
var respSignature ssh.Signature
err = json.Unmarshal(resp.Response, &respSignature)
if err != nil {
return nil, err
}
respSignature := ssh.Signature{Format: respMsg.SignatureFormat, Blob: respMsg.Signature}
return &respSignature, nil
}
@@ -64,6 +59,8 @@ type ROAgent struct {
signer ROSigner
}
// NewROAgent creates a new SSH agent which forwards signature requests to the
// provided remote server
func NewROAgent(server *client.RemoteServer, pubKey ssh.PublicKey, encryptedPrivKey []byte, user, pswd string) agent.Agent {
return &ROAgent{
ROSigner{
@@ -76,21 +73,22 @@ func NewROAgent(server *client.RemoteServer, pubKey ssh.PublicKey, encryptedPriv
}
}
// RemoveAll has no effect for the ROAgent
func (r *ROAgent) RemoveAll() error {
return nil
}
// Remove removes all identities with the given public key.
// Remove has no effect for the ROAgent
func (r *ROAgent) Remove(key ssh.PublicKey) error {
return nil
}
// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list.
// Lock has no effect for the ROAgent
func (r *ROAgent) Lock(passphrase []byte) error {
return nil
}
// Unlock undoes the effect of Lock
// Unlock has no effect for the ROAgent
func (r *ROAgent) Unlock(passphrase []byte) error {
return nil
}
@@ -101,14 +99,12 @@ func (r *ROAgent) List() ([]*agent.Key, error) {
{
Format: r.signer.PublicKey().Type(),
Blob: r.signer.PublicKey().Marshal(),
Comment: "",
Comment: "Red October encrypted SSH key",
},
}, nil
}
// Insert adds a private key to the ROAgent. If a certificate
// is given, that certificate is added as public key. Note that
// any constraints given are ignored.
// Add has no effect for the ROAgent
func (r *ROAgent) Add(key agent.AddedKey) error {
return nil
}

View File

@@ -61,21 +61,21 @@ func New(records *passvault.Records, cache *keycache.Cache, config *config.Confi
// both, then he can decrypt it alone). If a predicate is present, it must be
// satisfied to decrypt.
type AccessStructure struct {
Minimum int
Names []string
Minimum int
Names []string
LeftNames []string
RightNames []string
Predicate string
Predicate string
}
// Implements msp.UserDatabase
type UserDatabase struct {
names *[]string
names *[]string
records *passvault.Records
cache *keycache.Cache
records *passvault.Records
cache *keycache.Cache
user string
labels []string
@@ -129,6 +129,9 @@ type EncryptedData struct {
Version int
VaultId int `json:",omitempty"`
Labels []string `json:",omitempty"`
// Usages list the endpoints which may use this data
// If empty, only decryption in permitted
Usages []string `json:",omitempty"`
Predicate string `json:",omitempty"`
KeySet []MultiWrappedKey `json:",omitempty"`
KeySetRSA map[string]SingleWrappedKey `json:",omitempty"`
@@ -146,8 +149,8 @@ type pair struct {
type mwkSlice []MultiWrappedKey
type swkSlice []pair
func (s mwkSlice) Len() int { return len(s) }
func (s mwkSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s mwkSlice) Len() int { return len(s) }
func (s mwkSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s mwkSlice) Less(i, j int) bool { // Alphabetic order
var shorter = i
if len(s[i].Name) > len(s[j].Name) {
@@ -184,8 +187,9 @@ func (encrypted *EncryptedData) computeHmac(key []byte) []byte {
}
sort.Sort(&swks)
// sort the labels
// sort the labels and usages
sort.Strings(encrypted.Labels)
sort.Strings(encrypted.Usages)
// start hashing
mac.Write([]byte(strconv.Itoa(encrypted.Version)))
@@ -210,8 +214,13 @@ func (encrypted *EncryptedData) computeHmac(key []byte) []byte {
mac.Write(encrypted.Data)
// hash the labels
for index := range encrypted.Labels {
mac.Write([]byte(encrypted.Labels[index]))
for _, label := range encrypted.Labels {
mac.Write([]byte(label))
}
// hash the usages
for _, usage := range encrypted.Usages {
mac.Write([]byte(usage))
}
return mac.Sum(nil)
@@ -493,7 +502,7 @@ func (encrypted *EncryptedData) unwrapKey(cache *keycache.Cache, user string) (u
// 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 (c *Cryptor) Encrypt(in []byte, labels []string, access AccessStructure) (resp []byte, err error) {
func (c *Cryptor) Encrypt(in []byte, labels []string, usages []string, access AccessStructure) (resp []byte, err error) {
var encrypted EncryptedData
encrypted.Version = DEFAULT_VERSION
if encrypted.VaultId, err = c.records.GetVaultID(); err != nil {
@@ -530,6 +539,7 @@ func (c *Cryptor) Encrypt(in []byte, labels []string, access AccessStructure) (r
encrypted.Data = encryptedFile
encrypted.Labels = labels
encrypted.Usages = usages
hmacKey, err := c.records.GetHMACKey()
if err != nil {
@@ -542,18 +552,18 @@ func (c *Cryptor) Encrypt(in []byte, labels []string, access AccessStructure) (r
}
// Decrypt decrypts a file using the keys in the key cache.
func (c *Cryptor) Decrypt(in []byte, user string) (resp []byte, labels, names []string, secure bool, err error) {
func (c *Cryptor) Decrypt(in []byte, user string) (resp []byte, labels, names []string, usages []string, secure bool, err error) {
return c.decrypt(c.cache, in, user)
}
func (c *Cryptor) decrypt(cache *keycache.Cache, in []byte, user string) (resp []byte, labels, names []string, secure bool, err error) {
func (c *Cryptor) decrypt(cache *keycache.Cache, in []byte, user string) (resp []byte, labels, names []string, usages []string, secure bool, err error) {
// unwrap encrypted file
var encrypted EncryptedData
if err = json.Unmarshal(in, &encrypted); err != nil {
return
}
if encrypted.Version != DEFAULT_VERSION && encrypted.Version != -1 {
return nil, nil, nil, secure, errors.New("Unknown version")
return nil, nil, nil, nil, secure, errors.New("Unknown version")
}
secure = encrypted.Version == -1
@@ -573,7 +583,7 @@ func (c *Cryptor) decrypt(cache *keycache.Cache, in []byte, user string) (resp [
return
}
if encrypted.VaultId != vaultId {
return nil, nil, nil, secure, errors.New("Wrong vault")
return nil, nil, nil, nil, secure, errors.New("Wrong vault")
}
// compute HMAC
@@ -602,6 +612,7 @@ func (c *Cryptor) decrypt(cache *keycache.Cache, in []byte, user string) (resp [
resp, err = padding.RemovePadding(clearData)
labels = encrypted.Labels
usages = encrypted.Usages
return
}
@@ -726,7 +737,7 @@ func (c *Cryptor) store() error {
Predicate: c.persist.Policy(),
}
cache, err = c.Encrypt(cache, persist.Labels, access)
cache, err = c.Encrypt(cache, persist.Labels, persist.Usages, access)
if err != nil {
return err
}
@@ -764,7 +775,7 @@ func (c *Cryptor) Restore(name, password string, uses int, slot, durationString
// just means there aren't enough delegations yet; the
// sentinal value ErrRestoreDelegations is returned to
// indicate this. However, the error
cache, _, names, _, err := c.decrypt(c.persist.Cache(), c.persist.Blob(), name)
cache, _, _, names, _, err := c.decrypt(c.persist.Cache(), c.persist.Blob(), name)
if err != nil {
if err == msp.ErrNotEnoughShares {
return ErrRestoreDelegations

View File

@@ -111,7 +111,7 @@ func TestDuplicates(t *testing.T) {
RightNames: right,
}
resp, err := c.Encrypt([]byte("Hello World!"), []string{}, ac)
resp, err := c.Encrypt([]byte("Hello World!"), []string{}, []string{}, ac)
if err != nil {
t.Fatalf("Error: %s", err)
}