package sso import ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "testing" "time" "golang.org/x/crypto/ed25519" ) func TestEd25519(t *testing.T) { pub, priv, err := ed25519.GenerateKey(nil) if err != nil { t.Fatal(err) } signer := &ssoSigner{key: priv} validator := &ssoValidator{ publicKey: pub, domain: "domain", } tkt := NewTicket("user", "service", "domain", "nonce", nil, 300*time.Second) signed, err := signer.Sign(tkt) if err != nil { t.Fatal("Sign():", err) } if signed == "" { t.Fatal("signed is nil") } tkt2, err := validator.Validate(signed, "nonce", "service", nil) if err != nil { t.Fatal("Validate():", err) } if tkt.User != tkt2.User || tkt.Service != tkt2.Service || tkt.Domain != tkt2.Domain || tkt.Nonce != tkt2.Nonce || tkt.Expires != tkt2.Expires { t.Fatalf("decoded ticket differs: orig=%v, decoded=%v", tkt, tkt2) } } var ( legacyTicket = "yIo0k9TRhTT9F1C89nMbTDqJH905aMbN_O72ke2mWlss3vQAWffQWMRHoc0auYBGEex01fNFVRREEJALcZ15BzR8dXNlcnxzZXJ2aWNlfGRvbWFpbnx8MTUwODA3MjQyNXw" legacyPublicKey = []byte{47, 234, 144, 101, 76, 245, 1, 73, 155, 115, 89, 105, 165, 252, 49, 114, 48, 166, 231, 130, 82, 123, 147, 179, 50, 50, 34, 198, 219, 251, 151, 17} ) // TestLegacy verifies that tickets signed by the legacy C // implementation can be verified correctly by the Go code. func TestLegacy(t *testing.T) { validator := &ssoValidator{publicKey: legacyPublicKey} tkt, err := validator.parse(legacyTicket) if err != nil { t.Fatal("Parse():", err) } if tkt.User != "user" || tkt.Service != "service" || tkt.Domain != "domain" { t.Fatalf("decoded bad values: %+v", tkt) } } // Create a ssoSigner and sign a valid ticket. func makeTestSigner(t *testing.T) (string, string, func()) { dir, err := ioutil.TempDir("", "") if err != nil { t.Fatal(err) } pub, priv, err := ed25519.GenerateKey(nil) if err != nil { t.Fatal(err) } pubPath := filepath.Join(dir, "public.key") ioutil.WriteFile(pubPath, pub, 0600) //ioutil.WriteFile(filepath.Join(dir, "secret.key"), priv, 0600) tkt := NewTicket("user", "service/", "domain", "nonce", nil, 300*time.Second) signer := &ssoSigner{key: priv} signed, err := signer.Sign(tkt) if err != nil { t.Fatal("Sign():", err) } return dir, signed, func() { os.RemoveAll(dir) } } // TestLegacyIntegration verifies that tickets signed by the Go code // can be verified successfully by the legacy C implementation. // // To run this test, set the SSO_SRCDIR environment variable and point // it at the root of the ai/sso source package repository. func TestLegacyIntegration(t *testing.T) { ssoSrcDir := os.Getenv("SSO_SRCDIR") if ssoSrcDir == "" { t.Skip("SSO_SRCDIR not set") } ssotool := filepath.Join(ssoSrcDir, "src/sso/ssotool") if _, err := os.Stat(ssotool); os.IsNotExist(err) { t.Skip("ssotool not installed") } dir, signed, cleanup := makeTestSigner(t) defer cleanup() pubPath := filepath.Join(dir, "public.key") cmd := exec.Command(ssotool, "--public-key", pubPath, "--service", "service/", "--domain", "domain", "--nonce", "nonce", "--verify", signed) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { t.Fatalf("ssotool validation failed: %v", err) } } // TestLegacyIntegration verifies that tickets signed by the Go code // can be verified successfully by the legacy Python implementation. // // To run this test, set the SSO_SRCDIR environment variable and point // it at the root of the ai/sso source package repository. func TestLegacyIntegration_Python(t *testing.T) { ssoSrcDir := os.Getenv("SSO_SRCDIR") if ssoSrcDir == "" { t.Skip("SSO_SRCDIR not set") } pythonPath := fmt.Sprintf( "%s:%s", filepath.Join(ssoSrcDir, "src/python"), filepath.Join(ssoSrcDir, "src/python/sso"), ) libPath := filepath.Join(ssoSrcDir, "src/sso/.libs") dir, signed, cleanup := makeTestSigner(t) defer cleanup() pubPath := filepath.Join(dir, "public.key") scriptPath := filepath.Join(dir, "test.py") ioutil.WriteFile(scriptPath, []byte(` import sso import sys with open(sys.argv[1]) as fd: pubkey = fd.read() verifier = sso.Verifier(pubkey, "service/", "domain", None) tkt = verifier.verify(sys.argv[2].encode(), "nonce") sys.exit(0 if (tkt.user() == "user") else 1) `), 0600) cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("env PYTHONPATH=%s LD_LIBRARY_PATH=%s python %s %s %s", pythonPath, libPath, scriptPath, pubPath, signed)) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr err := cmd.Run() if err != nil { t.Fatalf("python validation failed: %v", err) } }