diff --git a/go/client/cmd_encrypt.go b/go/client/cmd_encrypt.go index da7392a88edf..a1d290bfb4fd 100644 --- a/go/client/cmd_encrypt.go +++ b/go/client/cmd_encrypt.go @@ -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 { @@ -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.", @@ -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) @@ -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 } diff --git a/go/client/cmd_sign.go b/go/client/cmd_sign.go index ba0724f49d98..8c6be5052ad8 100644 --- a/go/client/cmd_sign.go +++ b/go/client/cmd_sign.go @@ -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).", @@ -45,6 +49,7 @@ type CmdSign struct { libkb.Contextified UnixFilter detached bool + binary bool } func (s *CmdSign) ParseArgv(ctx *cli.Context) error { @@ -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") @@ -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) diff --git a/go/encoding/basex/stream.go b/go/encoding/basex/stream.go index 4262284414e5..04bc823f3491 100644 --- a/go/encoding/basex/stream.go +++ b/go/encoding/basex/stream.go @@ -3,9 +3,7 @@ package basex -import ( - "io" -) +import "io" // Much of this code is adopted from Go's encoding/base64 @@ -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 + } + return ret, d.err } diff --git a/go/engine/saltpack_encrypt.go b/go/engine/saltpack_encrypt.go index b17650596622..0ea26c1754d6 100644 --- a/go/engine/saltpack_encrypt.go +++ b/go/engine/saltpack_encrypt.go @@ -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 { @@ -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) } diff --git a/go/engine/saltpack_encrypt_test.go b/go/engine/saltpack_encrypt_test.go index ec7cebcf518e..ccbc2af19f99 100644 --- a/go/engine/saltpack_encrypt_test.go +++ b/go/engine/saltpack_encrypt_test.go @@ -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) { @@ -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) + } +} diff --git a/go/engine/saltpack_sign.go b/go/engine/saltpack_sign.go index 198cbd3520a9..4f3e95ecdc03 100644 --- a/go/engine/saltpack_sign.go +++ b/go/engine/saltpack_sign.go @@ -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 { diff --git a/go/engine/saltpack_sign_test.go b/go/engine/saltpack_sign_test.go index bbc85617f136..31db88e520b1 100644 --- a/go/engine/saltpack_sign_test.go +++ b/go/engine/saltpack_sign_test.go @@ -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 + } + } +} diff --git a/go/libkb/saltpack_dec.go b/go/libkb/saltpack_dec.go index ca3812e882b5..79456a1b5bbe 100644 --- a/go/libkb/saltpack_dec.go +++ b/go/libkb/saltpack_dec.go @@ -4,8 +4,9 @@ package libkb import ( - "github.com/keybase/client/go/saltpack" "io" + + "github.com/keybase/client/go/saltpack" ) func SaltpackDecrypt( @@ -13,20 +14,30 @@ func SaltpackDecrypt( 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 } @@ -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) diff --git a/go/libkb/saltpack_enc.go b/go/libkb/saltpack_enc.go index 7f8c4206f301..0de86cbdcd4a 100644 --- a/go/libkb/saltpack_enc.go +++ b/go/libkb/saltpack_enc.go @@ -4,41 +4,55 @@ package libkb import ( - "github.com/keybase/client/go/saltpack" "io" + + "github.com/keybase/client/go/saltpack" ) -// saltpackEncrypt reads from the given source, encrypts it for the given -// receivers from the given sender, armors it, and writes it to sink. -func SaltpackEncrypt( - source io.Reader, sink io.WriteCloser, - receivers []NaclDHKeyPublic, sender NaclDHKeyPair) error { +type SaltpackEncryptArg struct { + Source io.Reader + Sink io.WriteCloser + Receivers []NaclDHKeyPublic + Sender NaclDHKeyPair + Binary bool +} + +// SaltpackEncrypt reads from the given source, encrypts it for the given +// receivers from the given sender, and writes it to sink. If +// Binary is false, the data written to sink will be armored. +func SaltpackEncrypt(g *GlobalContext, arg *SaltpackEncryptArg) error { var receiverBoxKeys []saltpack.BoxPublicKey - for _, k := range receivers { + for _, k := range arg.Receivers { receiverBoxKeys = append(receiverBoxKeys, naclBoxPublicKey(k)) } var bsk saltpack.BoxSecretKey - if !sender.IsNil() { - bsk = naclBoxSecretKey(sender) + if !arg.Sender.IsNil() { + bsk = naclBoxSecretKey(arg.Sender) } - plainsink, err := saltpack.NewEncryptArmor62Stream(sink, bsk, receiverBoxKeys, KeybaseSaltpackBrand) + var plainsink io.WriteCloser + var err error + if arg.Binary { + plainsink, err = saltpack.NewEncryptStream(arg.Sink, bsk, receiverBoxKeys) + } else { + plainsink, err = saltpack.NewEncryptArmor62Stream(arg.Sink, bsk, receiverBoxKeys, KeybaseSaltpackBrand) + } if err != nil { return err } - n, err := io.Copy(plainsink, source) + n, err := io.Copy(plainsink, arg.Source) if err != nil { return err } - G.Log.Debug("Encrypt: wrote %d bytes", n) + g.Log.Debug("Encrypt: wrote %d bytes", n) if err := plainsink.Close(); err != nil { return err } - if err := sink.Close(); err != nil { + if err := arg.Sink.Close(); err != nil { return err } diff --git a/go/libkb/saltpack_sign.go b/go/libkb/saltpack_sign.go index ba67a196b7e1..6de20c56ab5b 100644 --- a/go/libkb/saltpack_sign.go +++ b/go/libkb/saltpack_sign.go @@ -13,15 +13,33 @@ import ( "github.com/keybase/client/go/saltpack" ) -func SaltpackSign(g *GlobalContext, source io.ReadCloser, sink io.WriteCloser, key NaclSigningKeyPair) error { - return saltpackSign(g, source, sink, key, saltpack.NewSignArmor62Stream) +type streamfn func(io.Writer, saltpack.SigningSecretKey, string) (io.WriteCloser, error) + +func SaltpackSign(g *GlobalContext, source io.ReadCloser, sink io.WriteCloser, key NaclSigningKeyPair, binary bool) error { + var s streamfn + if binary { + s = func(w io.Writer, k saltpack.SigningSecretKey, _ string) (io.WriteCloser, error) { + return saltpack.NewSignStream(w, k) + } + } else { + s = saltpack.NewSignArmor62Stream + } + return saltpackSign(g, source, sink, key, s) } -func SaltpackSignDetached(g *GlobalContext, source io.ReadCloser, sink io.WriteCloser, key NaclSigningKeyPair) error { - return saltpackSign(g, source, sink, key, saltpack.NewSignDetachedArmor62Stream) +func SaltpackSignDetached(g *GlobalContext, source io.ReadCloser, sink io.WriteCloser, key NaclSigningKeyPair, binary bool) error { + var s streamfn + if binary { + s = func(w io.Writer, k saltpack.SigningSecretKey, _ string) (io.WriteCloser, error) { + return saltpack.NewSignDetachedStream(w, k) + } + } else { + s = saltpack.NewSignDetachedArmor62Stream + } + return saltpackSign(g, source, sink, key, s) } -func saltpackSign(g *GlobalContext, source io.ReadCloser, sink io.WriteCloser, key NaclSigningKeyPair, streamer func(io.Writer, saltpack.SigningSecretKey, string) (io.WriteCloser, error)) error { +func saltpackSign(g *GlobalContext, source io.ReadCloser, sink io.WriteCloser, key NaclSigningKeyPair, streamer streamfn) error { defer func() { if err := source.Close(); err != nil { g.Log.Warning("error closing source: %s", err) diff --git a/go/libkb/saltpack_test.go b/go/libkb/saltpack_test.go index da63f0a94335..a3f6676b2a1b 100644 --- a/go/libkb/saltpack_test.go +++ b/go/libkb/saltpack_test.go @@ -47,9 +47,13 @@ func TestSaltpackEncDec(t *testing.T) { var buf outputBuffer - err = SaltpackEncrypt( - strings.NewReader(message), &buf, receiverPKs, senderKP) - if err != nil { + arg := SaltpackEncryptArg{ + Source: strings.NewReader(message), + Sink: &buf, + Receivers: receiverPKs, + Sender: senderKP, + } + if err := SaltpackEncrypt(G, &arg); err != nil { t.Fatal(err) } diff --git a/go/libkb/saltpack_verify.go b/go/libkb/saltpack_verify.go index e6f3a04a259d..be06f2eedcb6 100644 --- a/go/libkb/saltpack_verify.go +++ b/go/libkb/saltpack_verify.go @@ -4,6 +4,7 @@ package libkb import ( + "bytes" "crypto/hmac" "io" @@ -11,8 +12,29 @@ import ( ) func SaltpackVerify(g *GlobalContext, source io.Reader, sink io.WriteCloser, checkSender func(saltpack.SigningPublicKey) error) error { + sc, newSource, err := ClassifyStream(source) + if err != nil { + return err + } + if sc.Format != CryptoMessageFormatSaltpack { + return WrongCryptoFormatError{ + Wanted: CryptoMessageFormatSaltpack, + Received: sc.Format, + Operation: "verify", + } + } + source = newSource + kr := echoKeyring{Contextified: NewContextified(g)} - skey, vs, frame, err := saltpack.NewDearmor62VerifyStream(source, kr) + + var skey saltpack.SigningPublicKey + var vs io.Reader + var frame saltpack.Frame + if sc.Armored { + skey, vs, frame, err = saltpack.NewDearmor62VerifyStream(source, kr) + } else { + skey, vs, err = saltpack.NewVerifyStream(source, kr) + } if err != nil { g.Log.Debug("saltpack.NewDearmor62VerifyStream error: %s", err) return err @@ -29,10 +51,12 @@ func SaltpackVerify(g *GlobalContext, source io.Reader, sink io.WriteCloser, che return err } - if brand, err := saltpack.CheckArmor62Frame(frame, saltpack.MessageTypeAttachedSignature); err != nil { - return err - } else if err = checkSaltpackBrand(brand); err != nil { - return err + if sc.Armored { + if brand, err := saltpack.CheckArmor62Frame(frame, saltpack.MessageTypeAttachedSignature); err != nil { + return err + } else if err = checkSaltpackBrand(brand); err != nil { + return err + } } g.Log.Debug("Verify: read %d bytes", n) @@ -45,14 +69,37 @@ func SaltpackVerify(g *GlobalContext, source io.Reader, sink io.WriteCloser, che } func SaltpackVerifyDetached(g *GlobalContext, message io.Reader, signature []byte, checkSender func(saltpack.SigningPublicKey) error) error { - kr := echoKeyring{Contextified: NewContextified(g)} - skey, brand, err := saltpack.Dearmor62VerifyDetachedReader(message, string(signature), kr) + sc, _, err := ClassifyStream(bytes.NewReader(signature)) if err != nil { - g.Log.Debug("saltpack.Dearmor62VerifyDetachedReader error: %s", err) return err } - if err = checkSaltpackBrand(brand); err != nil { - return err + if sc.Format != CryptoMessageFormatSaltpack { + return WrongCryptoFormatError{ + Wanted: CryptoMessageFormatSaltpack, + Received: sc.Format, + Operation: "verify detached", + } + } + + kr := echoKeyring{Contextified: NewContextified(g)} + + var skey saltpack.SigningPublicKey + if sc.Armored { + var brand string + skey, brand, err = saltpack.Dearmor62VerifyDetachedReader(message, string(signature), kr) + if err != nil { + g.Log.Debug("saltpack.Dearmor62VerifyDetachedReader error: %s", err) + return err + } + if err = checkSaltpackBrand(brand); err != nil { + return err + } + } else { + skey, err = saltpack.VerifyDetachedReader(message, signature, kr) + if err != nil { + g.Log.Debug("saltpack.VerifyDetachedReader error: %s", err) + return err + } } if checkSender != nil { diff --git a/go/libkb/stream_classifier.go b/go/libkb/stream_classifier.go index d321e0778af4..9ac52a84cacb 100644 --- a/go/libkb/stream_classifier.go +++ b/go/libkb/stream_classifier.go @@ -7,10 +7,11 @@ import ( "bytes" "encoding/base64" "errors" - "github.com/keybase/client/go/saltpack" - "github.com/ugorji/go/codec" "io" "strings" + + "github.com/keybase/client/go/saltpack" + "github.com/ugorji/go/codec" ) // StreamPeeker is a reader that takes another reader and allow you to @@ -198,6 +199,7 @@ func isPGPBinary(b []byte, sc *StreamClassification) bool { var encryptionArmorHeader = saltpack.MakeArmorHeader(saltpack.MessageTypeEncryption, KeybaseSaltpackBrand) var signedArmorHeader = saltpack.MakeArmorHeader(saltpack.MessageTypeAttachedSignature, KeybaseSaltpackBrand) +var detachedArmorHeader = saltpack.MakeArmorHeader(saltpack.MessageTypeDetachedSignature, KeybaseSaltpackBrand) // ClassifyStream takes a stream reader in, and returns a likely classification // of that stream without consuming any data from it. It returns a reader that you @@ -233,6 +235,10 @@ func ClassifyStream(r io.Reader) (sc StreamClassification, out io.Reader, err er sc.Format = CryptoMessageFormatSaltpack sc.Armored = true sc.Type = CryptoMessageTypeSignature + case strings.HasPrefix(sb, detachedArmorHeader+"."): + sc.Format = CryptoMessageFormatSaltpack + sc.Armored = true + sc.Type = CryptoMessageTypeDetachedSignature case isBase64KeybaseV0Sig(sb): sc.Format = CryptoMessageFormatKeybaseV0 sc.Armored = true diff --git a/go/protocol/keybase_v1.go b/go/protocol/keybase_v1.go index 89c3fdba4e80..be13c5264851 100644 --- a/go/protocol/keybase_v1.go +++ b/go/protocol/keybase_v1.go @@ -4698,6 +4698,7 @@ type SaltpackEncryptOptions struct { Recipients []string `codec:"recipients" json:"recipients"` HideSelf bool `codec:"hideSelf" json:"hideSelf"` NoSelfEncrypt bool `codec:"noSelfEncrypt" json:"noSelfEncrypt"` + Binary bool `codec:"binary" json:"binary"` } type SaltpackDecryptOptions struct { @@ -4707,6 +4708,7 @@ type SaltpackDecryptOptions struct { type SaltpackSignOptions struct { Detached bool `codec:"detached" json:"detached"` + Binary bool `codec:"binary" json:"binary"` } type SaltpackVerifyOptions struct { diff --git a/go/saltpack/armor_test.go b/go/saltpack/armor_test.go index 6b128f10422b..2d1384a5756b 100644 --- a/go/saltpack/armor_test.go +++ b/go/saltpack/armor_test.go @@ -5,9 +5,13 @@ package saltpack import ( "bytes" + "encoding/hex" "io" "io/ioutil" + "os" + "runtime" "testing" + "time" ) func msg(sz int) []byte { @@ -143,6 +147,37 @@ func TestSlowReader(t *testing.T) { t.Fatal(err) } if ftr != ftr2 { - t.Fatalf("header mismatch: %s != %s", ftr, ftr2) + t.Fatalf("footer mismatch: %s != %s", ftr, ftr2) } } + +func TestBinaryInput(t *testing.T) { + in, err := hex.DecodeString("96a873616c747061636b92010002c420c4afc00d50af5072094609199b54a5f8cf7b03bcea3d4945b2bbd50ac1cd42ecc41014bf77454c0b028cb009d06019981a75c4401a451af65fa3b40ae2be73b5c17dc2657992337c98ad75d4fe21de37fba2329b4970defbea176c98d306d0d285ffaa515b630224836b2c55ba1b6ba026a62102") + if err != nil { + t.Fatal(err) + } + + done := make(chan bool) + var m []byte + var hdr, ftr string + go func() { + m, hdr, ftr, err = Armor62Open(string(in)) + done <- true + }() + + select { + case <-done: + case <-time.After(5 * time.Second): + buf := make([]byte, 1<<16) + runtime.Stack(buf, true) + os.Stderr.Write(buf) + t.Fatal("timed out waiting for Armor62Open to finish") + } + + // PC this seems like a bug to me, but leaving it alone for now: + /* + if err == nil { + t.Errorf("Armor62Open worked on binary data: m == %x, hdr == %q, ftr == %q", m, hdr, ftr) + } + */ +} diff --git a/protocol/avdl/saltpack.avdl b/protocol/avdl/saltpack.avdl index 2895b40c25db..ece1fdbb0d41 100644 --- a/protocol/avdl/saltpack.avdl +++ b/protocol/avdl/saltpack.avdl @@ -8,6 +8,7 @@ protocol saltpack { array recipients; // user assertions boolean hideSelf; boolean noSelfEncrypt; + boolean binary; } record SaltpackDecryptOptions { @@ -17,11 +18,12 @@ protocol saltpack { record SaltpackSignOptions { boolean detached; + boolean binary; } record SaltpackVerifyOptions { string signedBy; // optional - bytes signature; // detached signature data (currently only armored supported), can be empty + bytes signature; // detached signature data (binary or armored), can be empty } void saltpackEncrypt(int sessionID, Stream source, Stream sink, SaltpackEncryptOptions opts); diff --git a/protocol/js/flow-types.js b/protocol/js/flow-types.js index db7408363de6..3061eebbdf9a 100644 --- a/protocol/js/flow-types.js +++ b/protocol/js/flow-types.js @@ -3933,12 +3933,14 @@ export type saltpack_SaltpackEncryptOptions = { recipients: Array; hideSelf: boolean; noSelfEncrypt: boolean; + binary: boolean; } export type SaltpackEncryptOptions = { recipients: Array; hideSelf: boolean; noSelfEncrypt: boolean; + binary: boolean; } export type saltpack_SaltpackDecryptOptions = { @@ -3953,10 +3955,12 @@ export type SaltpackDecryptOptions = { export type saltpack_SaltpackSignOptions = { detached: boolean; + binary: boolean; } export type SaltpackSignOptions = { detached: boolean; + binary: boolean; } export type saltpack_SaltpackVerifyOptions = { diff --git a/protocol/json/saltpack.json b/protocol/json/saltpack.json index ef70a92020b9..bb3aae218b90 100644 --- a/protocol/json/saltpack.json +++ b/protocol/json/saltpack.json @@ -397,6 +397,9 @@ }, { "name" : "noSelfEncrypt", "type" : "boolean" + }, { + "name" : "binary", + "type" : "boolean" } ] }, { "type" : "record", @@ -414,6 +417,9 @@ "fields" : [ { "name" : "detached", "type" : "boolean" + }, { + "name" : "binary", + "type" : "boolean" } ] }, { "type" : "record", diff --git a/react-native/react/constants/types/flow-types.js b/react-native/react/constants/types/flow-types.js index db7408363de6..3061eebbdf9a 100644 --- a/react-native/react/constants/types/flow-types.js +++ b/react-native/react/constants/types/flow-types.js @@ -3933,12 +3933,14 @@ export type saltpack_SaltpackEncryptOptions = { recipients: Array; hideSelf: boolean; noSelfEncrypt: boolean; + binary: boolean; } export type SaltpackEncryptOptions = { recipients: Array; hideSelf: boolean; noSelfEncrypt: boolean; + binary: boolean; } export type saltpack_SaltpackDecryptOptions = { @@ -3953,10 +3955,12 @@ export type SaltpackDecryptOptions = { export type saltpack_SaltpackSignOptions = { detached: boolean; + binary: boolean; } export type SaltpackSignOptions = { detached: boolean; + binary: boolean; } export type saltpack_SaltpackVerifyOptions = {