Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binary support for saltpack commands #1727

Merged
merged 6 commits into from
Jan 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions go/client/cmd_encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type CmdEncrypt struct {
filter UnixFilter
recipients []string
noSelfEncrypt bool
binary bool
}

func NewCmdEncrypt(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
Expand All @@ -37,6 +38,10 @@ func NewCmdEncrypt(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Comma
// https://keybase.atlassian.net/browse/CORE-2142
// .
//
cli.BoolFlag{
Name: "b, binary",
Usage: "Output in binary (rather than ASCII/armored).",
},
cli.StringFlag{
Name: "i, infile",
Usage: "Specify an input file.",
Expand Down Expand Up @@ -80,6 +85,7 @@ func (c *CmdEncrypt) Run() error {
opts := keybase1.SaltpackEncryptOptions{
Recipients: c.recipients,
NoSelfEncrypt: c.noSelfEncrypt,
Binary: c.binary,
}
arg := keybase1.SaltpackEncryptArg{Source: src, Sink: snk, Opts: opts}
err = cli.SaltpackEncrypt(context.TODO(), arg)
Expand All @@ -105,6 +111,7 @@ func (c *CmdEncrypt) ParseArgv(ctx *cli.Context) error {
outfile := ctx.String("outfile")
infile := ctx.String("infile")
c.noSelfEncrypt = ctx.Bool("no-self")
c.binary = ctx.Bool("binary")
if err := c.filter.FilterInit(msg, infile, outfile); err != nil {
return err
}
Expand Down
7 changes: 7 additions & 0 deletions go/client/cmd_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ func NewCmdSign(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command
cl.ChooseCommand(&CmdSign{Contextified: libkb.NewContextified(g)}, "sign", c)
},
Flags: []cli.Flag{
cli.BoolFlag{
Name: "b, binary",
Usage: "Output binary message (default is armored).",
},
cli.BoolFlag{
Name: "d, detached",
Usage: "Detached signature (default is attached).",
Expand All @@ -45,6 +49,7 @@ type CmdSign struct {
libkb.Contextified
UnixFilter
detached bool
binary bool
}

func (s *CmdSign) ParseArgv(ctx *cli.Context) error {
Expand All @@ -53,6 +58,7 @@ func (s *CmdSign) ParseArgv(ctx *cli.Context) error {
}

s.detached = ctx.Bool("detached")
s.binary = ctx.Bool("binary")

msg := ctx.String("message")
outfile := ctx.String("outfile")
Expand Down Expand Up @@ -81,6 +87,7 @@ func (s *CmdSign) Run() (err error) {
Sink: snk,
Opts: keybase1.SaltpackSignOptions{
Detached: s.detached,
Binary: s.binary,
},
}
err = cli.SaltpackSign(context.TODO(), arg)
Expand Down
8 changes: 5 additions & 3 deletions go/encoding/basex/stream.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@

package basex

import (
"io"
)
import "io"

// Much of this code is adopted from Go's encoding/base64

Expand Down Expand Up @@ -190,6 +188,10 @@ func (d *decoder) Read(p []byte) (int, error) {
d.nbuf -= numBytesToDecode
copy(d.buf[0:d.nbuf], d.buf[numBytesToDecode:numBytesToDecode+d.nbuf])

if ret == 0 && d.err == nil && len(p) != 0 {
return 0, io.EOF
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did this fix a bug you ran into?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Patrick mentioned something about today on the slack channel.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, see slack and TestBinaryInput in armor_test.go

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was an infinite loop without this.


return ret, d.err
}

Expand Down
12 changes: 10 additions & 2 deletions go/engine/saltpack_encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
package engine

import (
"io"

"github.com/keybase/client/go/libkb"
keybase1 "github.com/keybase/client/go/protocol"
"io"
)

type SaltpackEncryptArg struct {
Expand Down Expand Up @@ -147,5 +148,12 @@ func (e *SaltpackEncrypt) Run(ctx *Context) (err error) {
sender = kp
}

return libkb.SaltpackEncrypt(e.arg.Source, e.arg.Sink, receivers, sender)
encarg := libkb.SaltpackEncryptArg{
Source: e.arg.Source,
Sink: e.arg.Sink,
Receivers: receivers,
Sender: sender,
Binary: e.arg.Opts.Binary,
}
return libkb.SaltpackEncrypt(e.G(), &encarg)
}
49 changes: 47 additions & 2 deletions go/engine/saltpack_encrypt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
package engine

import (
"github.com/keybase/client/go/libkb"
keybase1 "github.com/keybase/client/go/protocol"
"strings"
"testing"

"github.com/keybase/client/go/libkb"
keybase1 "github.com/keybase/client/go/protocol"
)

func TestSaltpackEncrypt(t *testing.T) {
Expand Down Expand Up @@ -203,3 +204,47 @@ func TestSaltpackEncryptNoSelf(t *testing.T) {
t.Errorf("decoded: %s, expected: %s", decmsg, msg)
}
}

func TestSaltpackEncryptBinary(t *testing.T) {
tc := SetupEngineTest(t, "SaltpackEncryptBinary")
defer tc.Cleanup()
fu := CreateAndSignupFakeUser(tc, "enc")

// encrypt a message
msg := "10 days in Japan"
sink := libkb.NewBufferCloser()
ctx := &Context{
IdentifyUI: &FakeIdentifyUI{},
SecretUI: fu.NewSecretUI(),
LogUI: tc.G.UI.GetLogUI(),
SaltpackUI: &fakeSaltpackUI{},
}
// Should encrypt for self, too.
arg := &SaltpackEncryptArg{
Source: strings.NewReader(msg),
Sink: sink,
Opts: keybase1.SaltpackEncryptOptions{
Binary: true,
},
}
enc := NewSaltpackEncrypt(arg, tc.G)
if err := RunEngine(enc, ctx); err != nil {
t.Fatal(err)
}
out := sink.String()

// decrypt it
decoded := libkb.NewBufferCloser()
decarg := &SaltpackDecryptArg{
Source: strings.NewReader(out),
Sink: decoded,
}
dec := NewSaltpackDecrypt(decarg, tc.G)
if err := RunEngine(dec, ctx); err != nil {
t.Fatal(err)
}
decmsg := decoded.String()
if decmsg != msg {
t.Errorf("decoded: %s, expected: %s", decmsg, msg)
}
}
4 changes: 2 additions & 2 deletions go/engine/saltpack_sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ func (e *SaltpackSign) Run(ctx *Context) error {
}

if e.arg.Opts.Detached {
return libkb.SaltpackSignDetached(e.G(), e.arg.Source, e.arg.Sink, e.key)
return libkb.SaltpackSignDetached(e.G(), e.arg.Source, e.arg.Sink, e.key, e.arg.Opts.Binary)
}

return libkb.SaltpackSign(e.G(), e.arg.Source, e.arg.Sink, e.key)
return libkb.SaltpackSign(e.G(), e.arg.Source, e.arg.Sink, e.key, e.arg.Opts.Binary)
}

func (e *SaltpackSign) loadKey(ctx *Context) error {
Expand Down
98 changes: 98 additions & 0 deletions go/engine/saltpack_sign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,101 @@ func TestSaltpackSignVerify(t *testing.T) {
}
}
}

func TestSaltpackSignVerifyBinary(t *testing.T) {
tc := SetupEngineTest(t, "sign")
defer tc.Cleanup()

fu := CreateAndSignupFakeUser(tc, "sign")

// signTests are defined in pgp_sign_test. Make sure that saltpack sign can
// sign/verify the same messages as pgp.
for _, test := range signTests {
var sink bytes.Buffer

sarg := &SaltpackSignArg{
Sink: libkb.NopWriteCloser{W: &sink},
Source: ioutil.NopCloser(bytes.NewBufferString(test.input)),
Opts: keybase1.SaltpackSignOptions{
Binary: true,
},
}

eng := NewSaltpackSign(sarg, tc.G)
ctx := &Context{
IdentifyUI: &FakeIdentifyUI{},
SecretUI: fu.NewSecretUI(),
}

if err := RunEngine(eng, ctx); err != nil {
t.Errorf("%s: run error: %s", test.name, err)
continue
}

sig := sink.String()

if len(sig) == 0 {
t.Errorf("%s: empty sig", test.name)
}

varg := &SaltpackVerifyArg{
Sink: libkb.NopWriteCloser{W: &sink},
Source: strings.NewReader(sig),
}
veng := NewSaltpackVerify(varg, tc.G)

ctx.SaltpackUI = fakeSaltpackUI{}

if err := RunEngine(veng, ctx); err != nil {
t.Errorf("%s: verify error: %s", test.name, err)
continue
}
}

// now try the same messages, but generate detached signatures
for _, test := range signTests {
var sink bytes.Buffer

sarg := &SaltpackSignArg{
Sink: libkb.NopWriteCloser{W: &sink},
Source: ioutil.NopCloser(bytes.NewBufferString(test.input)),
Opts: keybase1.SaltpackSignOptions{
Binary: true,
Detached: true,
},
}

eng := NewSaltpackSign(sarg, tc.G)
ctx := &Context{
IdentifyUI: &FakeIdentifyUI{},
SecretUI: fu.NewSecretUI(),
}

if err := RunEngine(eng, ctx); err != nil {
t.Errorf("(detached) %s: run error: %s", test.name, err)
continue
}

sig := sink.Bytes()

if len(sig) == 0 {
t.Errorf("(detached) %s: empty sig", test.name)
}

varg := &SaltpackVerifyArg{
Sink: libkb.NopWriteCloser{W: &sink},
Source: strings.NewReader(test.input),
Opts: keybase1.SaltpackVerifyOptions{
Signature: sig,
},
}
veng := NewSaltpackVerify(varg, tc.G)

ctx.SaltpackUI = fakeSaltpackUI{}

if err := RunEngine(veng, ctx); err != nil {
t.Errorf("(detached) %s: verify error: %s", test.name, err)
continue
}
}
}
39 changes: 26 additions & 13 deletions go/libkb/saltpack_dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,40 @@
package libkb

import (
"github.com/keybase/client/go/saltpack"
"io"

"github.com/keybase/client/go/saltpack"
)

func SaltpackDecrypt(
g *GlobalContext, source io.Reader, sink io.WriteCloser,
deviceEncryptionKey NaclDHKeyPair,
checkSender func(*saltpack.MessageKeyInfo) error) (*saltpack.MessageKeyInfo, error) {

if sc, newSource, err := ClassifyStream(source); err != nil {
sc, newSource, err := ClassifyStream(source)
if err != nil {
return nil, err
} else if sc.Format != CryptoMessageFormatSaltpack {
}

if sc.Format != CryptoMessageFormatSaltpack {
return nil, WrongCryptoFormatError{
Wanted: CryptoMessageFormatSaltpack,
Received: sc.Format,
Operation: "decrypt",
}
}

source = newSource

var mki *saltpack.MessageKeyInfo
var plainsource io.Reader
var frame saltpack.Frame
if sc.Armored {
mki, plainsource, frame, err = saltpack.NewDearmor62DecryptStream(source, naclKeyring(deviceEncryptionKey))
} else {
source = newSource
mki, plainsource, err = saltpack.NewDecryptStream(source, naclKeyring(deviceEncryptionKey))
}

mki, plainsource, frame, err := saltpack.NewDearmor62DecryptStream(
source, naclKeyring(deviceEncryptionKey))
if err != nil {
return mki, err
}
Expand All @@ -44,13 +55,15 @@ func SaltpackDecrypt(

// TODO: Check header inline, and only warn if the footer
// doesn't match.
var brand string
brand, err = saltpack.CheckArmor62Frame(frame, saltpack.MessageTypeEncryption)
if err != nil {
return mki, err
}
if err = checkSaltpackBrand(brand); err != nil {
return mki, err
if sc.Armored {
var brand string
brand, err = saltpack.CheckArmor62Frame(frame, saltpack.MessageTypeEncryption)
if err != nil {
return mki, err
}
if err = checkSaltpackBrand(brand); err != nil {
return mki, err
}
}

g.Log.Debug("Decrypt: read %d bytes", n)
Expand Down
Loading