// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package subtle_test import ( "crypto/rand" "encoding/hex" "fmt" "testing" "golang.org/x/crypto/curve25519" "github.com/google/tink/go/subtle" "github.com/google/tink/go/testutil" ) func TestComputeSharedSecretX25519WithRFCTestVectors(t *testing.T) { // Test vectors are defined at // https://datatracker.ietf.org/doc/html/rfc7748#section-6.1. tests := []struct { priv string pub string }{ {"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"}, {"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"}, } shared := "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742" for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { priv, err := hex.DecodeString(test.priv) if err != nil { t.Fatalf("DecodeString(priv): got err %q, want nil", err) } pub, err := hex.DecodeString(test.pub) if err != nil { t.Fatalf("DecodeString(pub): got err %q, want nil", err) } gotShared, err := subtle.ComputeSharedSecretX25519(priv, pub) if err != nil { t.Fatalf("ComputeSharedSecretX25519(priv, pub): got err %q, want nil", err) } if got, want := hex.EncodeToString(gotShared), shared; got != want { t.Errorf("ComputeSharedSecretX25519(shared): got %v, want %v", got, want) } }) } } type x25519Suite struct { testutil.WycheproofSuite TestGroups []*x25519Group `json:"testGroups"` } type x25519Group struct { testutil.WycheproofGroup Curve string `json:"curve"` Tests []*x25519Case `json:"tests"` } type x25519Case struct { testutil.WycheproofCase Public string `json:"public"` Private string `json:"private"` Shared string `json:"shared"` Result string `json:"result"` Flags []string `json:"flags"` } func TestComputeSharedSecretX25519WithWycheproofVectors(t *testing.T) { testutil.SkipTestIfTestSrcDirIsNotSet(t) suite := new(x25519Suite) if err := testutil.PopulateSuite(suite, "x25519_test.json"); err != nil { t.Fatalf("testutil.PopulateSuite: %v", err) } for _, group := range suite.TestGroups { if group.Curve != "curve25519" { continue } for _, test := range group.Tests { t.Run(fmt.Sprintf("%d", test.CaseID), func(t *testing.T) { pub, err := hex.DecodeString(test.Public) if err != nil { t.Fatalf("DecodeString(pub): got err %q, want nil", err) } priv, err := hex.DecodeString(test.Private) if err != nil { t.Fatalf("DecodeString(priv): got err %q, want nil", err) } gotShared, err := subtle.ComputeSharedSecretX25519(priv, pub) // ComputeSharedSecretX25519 fails on low order public values. wantErr := false for _, flag := range test.Flags { if flag == "LowOrderPublic" { wantErr = true } } if wantErr { if err == nil { t.Error("ComputeSharedSecretX25519(priv, pub): got success, want err") } } else { if err != nil { t.Errorf("ComputeSharedSecretX25519(priv, pub): got err %q, want nil", err) } if got, want := hex.EncodeToString(gotShared), test.Shared; got != want { t.Errorf("ComputeSharedSecretX25519(shared): got %v, want %v", got, want) } } }) } } } func TestComputeSharedSecretX25519Fails(t *testing.T) { pubs := []string{ // Should fail on non-32-byte inputs. "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c", "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a2a", // Should fail on low order points, from Sodium // https://github.com/jedisct1/libsodium/blob/65621a1059a37d/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c#L11-L70. "0000000000000000000000000000000000000000000000000000000000000000", "0100000000000000000000000000000000000000000000000000000000000000", "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800", "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157", "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", } priv := make([]byte, curve25519.ScalarSize) if _, err := rand.Read(priv); err != nil { t.Fatal(err) } for i, pubHex := range pubs { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { pub, err := hex.DecodeString(pubHex) if err != nil { t.Fatalf("DecodeString(pub): got err %q, want nil", err) } if _, err := subtle.ComputeSharedSecretX25519(priv, pub); err == nil { t.Error("ComputeSharedSecretX25519(priv, pub): got success, want err") } }) } } func TestPublicFromPrivateX25519WithRFCTestVectors(t *testing.T) { // Test vectors are defined at // https://datatracker.ietf.org/doc/html/rfc7748#section-6.1. tests := []struct { priv string pub string }{ {"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"}, {"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"}, } for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { priv, err := hex.DecodeString(test.priv) if err != nil { t.Fatalf("DecodeString(priv): got err %q, want nil", err) } gotPub, err := subtle.PublicFromPrivateX25519(priv) if err != nil { t.Fatalf("PublicFromPrivateX25519(priv): got err %q, want nil", err) } if got, want := hex.EncodeToString(gotPub), test.pub; got != want { t.Errorf("PublicFromPrivateX25519(priv): got %s, want %s", got, want) } }) } } func TestPublicFromPrivateX25519Fails(t *testing.T) { // PublicFromPrivateX25519 fails on non-32-byte private keys. privs := []string{ "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c", "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb95", } for i, priv := range privs { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { priv, err := hex.DecodeString(priv) if err != nil { t.Fatalf("DecodeString(priv): got err %q, want nil", err) } if _, err := subtle.PublicFromPrivateX25519(priv); err == nil { t.Error("PublicFromPrivateX25519(priv): got success, want err") } }) } }