jsonsign: more tests

This commit is contained in:
Brad Fitzpatrick 2011-03-15 18:01:04 -07:00
parent 33e2d32f99
commit ae15620ea2
4 changed files with 98 additions and 43 deletions

View File

@ -62,16 +62,50 @@ iNSWzNQiTT6k7fumOABCoSZsow/AJxQSxqKOJBjgpKjIKCgY
=ru0J
-----END PGP PUBLIC KEY BLOCK-----`
var pubKeyBlob1 = &blobref.TestBlob{pubKey1}
var pubKey2 = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1.4.10 (GNU/Linux)
mQENBEz61lcBCADRQhcb9LIQdV3LhU5f7cCjOctmLsL+y4k4VKmznssWORiNPEHQ
13CxFLjRDN2OQYXi4NSqoUqHNMsRTUJTVW0CnznUUb11ibXLUYW/zbPN9dWs8PlI
UZSScS1dxtGKKk+VfXrvc1LB6pqrjWmAgEwQxsBWToW2IFR/eMo1LiVU83dzpKU1
n/yb8Jy9wizchspd9xecK2X0JnKLRIJklLTAKQ+XKP+cSwXmShcs+3pxu5f4piqF
7oBfh9noFA0vdGYNBGVch3DfJwFcTmLkkGFZKdiehWncvVYT1jxUkJvc0K44ohDH
smkG2VZm3rJCwi2GIWA/clLiDAhYM6vTI3oZABEBAAG0K0NhbWxpIFRlc3R1c2Vy
IFR3byA8Y2FtbGkudGVzdEBleGFtcGxlLmNvbT6JATgEEwECACIFAkz61lcCGwMG
CwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEIUeCLJL7Fq1c44IAKOJjymoinXd
9NOW7GfpHCmynzSflJJoRcRzsNz83lJbwITYCd1ExQxkO84sMKRPJiefc9epP/Hg
8V4b1SwkGi+A8WaoH/OZtEM8HA7iEKmV+wjfZE6kt+y0trbxdu42W5hLz/uerrNl
G+r90mBNjmJXsZxmwaZEFrLtFlqezCzdQSur35QLZMFvW6aoYFTAgOk1rk9lBtkC
DePaadZQGHNWr+Rw2M5xXv9BZ4Rrjl6VLjE2DuqMSBVkelckBcsmRppaszF3J8y3
9gd10xC+5/LVfhU8niDZjY3pIcjQwsYJ+Jdyce2OEYo1i6pQDiq2WewXdCJ28DVK
1SX38WFB3Zm5AQ0ETPrWVwEIAMQ/dRCrkhy2D0SzJV5o/Z3uVf1nFLlEFfavV45F
8wtG/Bi5EuZXoYqU+O79O7sPy9Dw3Qhxtvt159l6/sSLXYTBBs3HJ2zTVhI5tbAZ
DMz4/wfkRP/h74KuXnWfin1ynswzqdPVXgrRvTsfHbkwbTaRwbx186VYqM17Wqy2
hFAUCdQIIW0+X9upjGek+kESldSzeUV87fr3IN/pq6fRc90h8xAKfz6mMc7AAUUL
NLNxb9y18u4Bw+fKgc6W7YxB+gQN1IajmgGPcqUTxNxydWF974iqsKnkZpzHg0Ce
zGGLWzCAGzI8drltgJPBoGGo56U1s2hW6JzLUi03phV10H8AEQEAAYkBHwQYAQIA
CQUCTPrWVwIbDAAKCRCFHgiyS+xatUPIB/9VPOeIxH5UcNYuZT+LW2tdcWPNhyQ+
u5UC9DC2A3F9AYNYRwDcSVOMmqS8hPJxg/biFxFoGFgm14Vp0nd1blOHcmNXcDzk
XTv2CKcUbgYpvDVmfCcEf6seSf+/RDbyj/VzebE6yvXuwsPus7ntbMw+Dum42z55
XYiYsfEFu25RtxritG3eYklCKymdRg615pj8zoRpL5Z1NAy5QBb5sv5hPbdGSyqL
Kw6aLcq2IU7kev6CYJVyXzJ1XtsYv/o7hzKKmZ5WcwuPc9Yqh6onJt1RC8jzz8Ry
jyVNPb8AaaWVW1uZLg6Em61aKnbOG10B30m3CQ8dwBjF9hgmtcY0IZ/Y
=OWHA
-----END PGP PUBLIC KEY BLOCK-----
`
var pubKeyBlob1 = &blobref.TestBlob{pubKey1} // user 1
var pubKeyBlob2 = &blobref.TestBlob{pubKey2} // user 2
var testFetcher = &TestFetcher{}
func init() {
testFetcher.AddBlob(pubKeyBlob1)
testFetcher.AddBlob(pubKeyBlob2)
}
func TestSigningBadInput(t *testing.T) {
sr := newRequest()
sr := newRequest(1)
sr.UnsignedJson = ""
_, err := sr.Sign()
@ -90,29 +124,48 @@ func TestSigningBadInput(t *testing.T) {
ExpectErrorContains(t, err, "\"camliSigner\" key is malformed or unsupported", "empty camliSigner")
}
func newRequest() *SignRequest {
func newRequest(userN int) *SignRequest {
if userN < 1 || userN > 2 {
panic("invalid userid")
}
suffix := ".gpg"
if userN == 2 {
suffix = "2.gpg"
}
return &SignRequest{
UnsignedJson: "",
Fetcher: testFetcher,
UseAgent: false,
ServerMode: true,
SecretKeyringPath: "./testdata/test-secring.gpg",
KeyringPath: "./testdata/test-keyring.gpg",
SecretKeyringPath: "./testdata/test-secring" + suffix,
KeyringPath: "./testdata/test-keyring" + suffix,
}
}
func TestSigning(t *testing.T) {
sr := newRequest()
sr.UnsignedJson = fmt.Sprintf(`{"camliVersion": 1, "camliSigner": %q }`, pubKeyBlob1.BlobRef().String())
sr := newRequest(1)
sr.UnsignedJson = fmt.Sprintf(`{"camliVersion": 1, "foo": "fooVal", "camliSigner": %q }`, pubKeyBlob1.BlobRef().String())
signed, err := sr.Sign()
AssertNil(t, err, "no error signing")
Assert(t, strings.Contains(signed, `"camliSig":`), "got a camliSig")
vr := NewVerificationRequest(signed, testFetcher)
if !vr.Verify() {
t.Errorf("verification failed on signed json [%s]: %v", signed, vr.Err)
t.Fatalf("verification failed on signed json [%s]: %v", signed, vr.Err)
}
t.Logf("TODO: finish these tests; verify things round-trip, verify GPG external-vs-Go sign & verify round-trip, test signatures from wrong signer don't verify, etc.")
ExpectString(t, "fooVal", vr.PayloadMap["foo"].(string), "PayloadMap")
ExpectString(t, "2931A67C26F5ABDA", vr.SignerKeyId, "SignerKeyId")
// Test a non-matching signature.
fakeSigned := strings.Replace(signed, pubKeyBlob1.BlobRef().String(), pubKeyBlob2.BlobRef().String(), 1)
vr = NewVerificationRequest(fakeSigned, testFetcher)
if vr.Verify() {
t.Fatalf("unexpected verification of faked signature")
}
AssertErrorContains(t, vr.Err, "OpenPGP signature invalid: hash tag doesn't match",
"expected signature verification error")
t.Logf("TODO: verify GPG-vs-Go sign & verify interop both ways, once implemented.")
}
type TestFetcher struct {

View File

@ -21,7 +21,7 @@ import (
"os"
"io"
"crypto/openpgp/armor"
"crypto/openpgp/packet"
"crypto/openpgp/packet"
"strings"
)

View File

@ -39,16 +39,16 @@ var flagSecretRing *string = flag.String("secret-keyring", "./test/test-secring.
type SignRequest struct {
UnsignedJson string
Fetcher blobref.Fetcher
UseAgent bool
UnsignedJson string
Fetcher blobref.Fetcher
UseAgent bool
// In server-mode, don't use any default (user) keys
// TODO: formalize what this means?
ServerMode bool
ServerMode bool
SecretKeyringPath string
KeyringPath string
KeyringPath string
}
func (sr *SignRequest) publicRingPath() string {
@ -109,12 +109,12 @@ func (sr *SignRequest) Sign() (signedJson string, err os.Error) {
if len(trimmedJson) == 0 || trimmedJson[len(trimmedJson)-1] != '}' {
return inputfail("json parameter lacks trailing '}'")
}
trimmedJson = trimmedJson[0:len(trimmedJson)-1]
trimmedJson = trimmedJson[0 : len(trimmedJson)-1]
args := []string{"gpg",
"--local-user", fmt.Sprintf("%X", pk.Fingerprint[len(pk.Fingerprint)-4:]),
"--detach-sign",
"--armor"}
"--local-user", fmt.Sprintf("%X", pk.Fingerprint[len(pk.Fingerprint)-4:]),
"--detach-sign",
"--armor"}
if sr.UseAgent {
args = append(args, "--use-agent")
@ -134,9 +134,9 @@ func (sr *SignRequest) Sign() (signedJson string, err os.Error) {
args,
os.Environ(),
".",
exec.Pipe, // stdin
exec.Pipe, // stdout
exec.Pipe) // stderr
exec.Pipe, // stdin
exec.Pipe, // stdout
exec.Pipe) // stderr
if err != nil {
return execfail("Failed to run gpg.")
}
@ -162,12 +162,11 @@ func (sr *SignRequest) Sign() (signedJson string, err os.Error) {
index1 := strings.Index(output, "\n\n")
index2 := strings.Index(output, "\n-----")
if (index1 == -1 || index2 == -1) {
if index1 == -1 || index2 == -1 {
return execfail("Failed to parse signature from gpg.")
}
inner := output[index1+2:index2]
inner := output[index1+2 : index2]
signature := strings.Replace(inner, "\n", "", -1)
return fmt.Sprintf("%s,\"camliSig\":\"%s\"}\n", trimmedJson, signature), nil
}

View File

@ -27,8 +27,8 @@ import (
"log"
"os"
"strings"
"crypto/sha1"
)
"crypto/sha1"
)
var logf = log.Printf
@ -60,20 +60,22 @@ func reArmor(line string) string {
// See doc/json-signing/* for background and details
// on these variable names.
type VerifyRequest struct {
fetcher blobref.Fetcher // fetcher used to find public key blob
fetcher blobref.Fetcher // fetcher used to find public key blob
ba []byte // "bytes all"
bp []byte // "bytes payload" (the part that is signed)
bpj []byte // "bytes payload, JSON" (BP + "}")
bs []byte // "bytes signature", "{" + separator + camliSig, valid JSON
CamliSigner *blobref.BlobRef
PayloadMap map[string]interface{} // The JSON values from BPJ
CamliSig string
ba []byte // "bytes all"
bp []byte // "bytes payload" (the part that is signed)
bpj []byte // "bytes payload, JSON" (BP + "}")
bs []byte // "bytes signature", "{" + separator + camliSig, valid JSON
CamliSigner *blobref.BlobRef
CamliSig string
PublicKeyPacket *packet.PublicKey
Err os.Error // last error encountered
// set if Verify() returns true:
PayloadMap map[string]interface{} // The JSON values from BPJ
SignerKeyId string
Err os.Error // last error encountered
}
func (vr *VerifyRequest) fail(msg string) bool {
@ -143,7 +145,7 @@ func (vr *VerifyRequest) FindAndParsePublicKeyBlob() bool {
return vr.fail(fmt.Sprintf("error opening public key file: %v", err))
}
vr.PublicKeyPacket = pk
return true;
return true
}
func (vr *VerifyRequest) VerifySignature() bool {
@ -169,11 +171,12 @@ func (vr *VerifyRequest) VerifySignature() bool {
return vr.fail("I can only verify binary signatures")
}
hash := sha1.New()
hash.Write(vr.bp) // payload bytes
hash.Write(vr.bp) // payload bytes
err = vr.PublicKeyPacket.VerifySignature(hash, sig)
if err != nil {
return vr.fail(fmt.Sprintf("bad signature: %s", err))
}
vr.SignerKeyId = vr.PublicKeyPacket.KeyIdString()
return true
}
@ -181,7 +184,7 @@ func NewVerificationRequest(sjson string, fetcher blobref.Fetcher) (vr *VerifyRe
vr = new(VerifyRequest)
vr.ba = []byte(sjson)
vr.fetcher = fetcher
sigIndex := bytes.LastIndex(vr.ba, []byte(sigSeparator))
if sigIndex == -1 {
vr.Err = os.NewError("jsonsign: no 13-byte camliSig separator found in sjson")
@ -189,12 +192,12 @@ func NewVerificationRequest(sjson string, fetcher blobref.Fetcher) (vr *VerifyRe
}
// "Bytes Payload"
vr.bp = vr.ba[0:sigIndex]
vr.bp = vr.ba[:sigIndex]
// "Bytes Payload JSON". Note we re-use the memory (the ",")
// from BA in BPJ, so we can't re-use that "," byte for
// the opening "{" in "BS".
vr.bpj = vr.ba[0:sigIndex+1]
vr.bpj = vr.ba[:sigIndex+1]
vr.bpj[sigIndex] = '}'
vr.bs = []byte("{" + sjson[sigIndex+1:])
return
@ -209,7 +212,7 @@ func (vr *VerifyRequest) Verify() bool {
vr.ParsePayloadMap() &&
vr.FindAndParsePublicKeyBlob() &&
vr.VerifySignature() {
return true;
return true
}
// Don't allow dumbs callers to accidentally check this