1// Copyright 2021 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14// 15//////////////////////////////////////////////////////////////////////////////// 16 17package subtle_test 18 19import ( 20 "crypto/rand" 21 "encoding/hex" 22 "fmt" 23 "testing" 24 25 "golang.org/x/crypto/curve25519" 26 "github.com/google/tink/go/subtle" 27 "github.com/google/tink/go/testutil" 28) 29 30func TestComputeSharedSecretX25519WithRFCTestVectors(t *testing.T) { 31 // Test vectors are defined at 32 // https://datatracker.ietf.org/doc/html/rfc7748#section-6.1. 33 tests := []struct { 34 priv string 35 pub string 36 }{ 37 {"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"}, 38 {"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"}, 39 } 40 shared := "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742" 41 42 for i, test := range tests { 43 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 44 priv, err := hex.DecodeString(test.priv) 45 if err != nil { 46 t.Fatalf("DecodeString(priv): got err %q, want nil", err) 47 } 48 pub, err := hex.DecodeString(test.pub) 49 if err != nil { 50 t.Fatalf("DecodeString(pub): got err %q, want nil", err) 51 } 52 53 gotShared, err := subtle.ComputeSharedSecretX25519(priv, pub) 54 if err != nil { 55 t.Fatalf("ComputeSharedSecretX25519(priv, pub): got err %q, want nil", err) 56 } 57 if got, want := hex.EncodeToString(gotShared), shared; got != want { 58 t.Errorf("ComputeSharedSecretX25519(shared): got %v, want %v", got, want) 59 } 60 }) 61 } 62} 63 64type x25519Suite struct { 65 testutil.WycheproofSuite 66 TestGroups []*x25519Group `json:"testGroups"` 67} 68 69type x25519Group struct { 70 testutil.WycheproofGroup 71 Curve string `json:"curve"` 72 Tests []*x25519Case `json:"tests"` 73} 74 75type x25519Case struct { 76 testutil.WycheproofCase 77 Public string `json:"public"` 78 Private string `json:"private"` 79 Shared string `json:"shared"` 80 Result string `json:"result"` 81 Flags []string `json:"flags"` 82} 83 84func TestComputeSharedSecretX25519WithWycheproofVectors(t *testing.T) { 85 testutil.SkipTestIfTestSrcDirIsNotSet(t) 86 87 suite := new(x25519Suite) 88 if err := testutil.PopulateSuite(suite, "x25519_test.json"); err != nil { 89 t.Fatalf("testutil.PopulateSuite: %v", err) 90 } 91 92 for _, group := range suite.TestGroups { 93 if group.Curve != "curve25519" { 94 continue 95 } 96 97 for _, test := range group.Tests { 98 t.Run(fmt.Sprintf("%d", test.CaseID), func(t *testing.T) { 99 pub, err := hex.DecodeString(test.Public) 100 if err != nil { 101 t.Fatalf("DecodeString(pub): got err %q, want nil", err) 102 } 103 priv, err := hex.DecodeString(test.Private) 104 if err != nil { 105 t.Fatalf("DecodeString(priv): got err %q, want nil", err) 106 } 107 108 gotShared, err := subtle.ComputeSharedSecretX25519(priv, pub) 109 // ComputeSharedSecretX25519 fails on low order public values. 110 wantErr := false 111 for _, flag := range test.Flags { 112 if flag == "LowOrderPublic" { 113 wantErr = true 114 } 115 } 116 117 if wantErr { 118 if err == nil { 119 t.Error("ComputeSharedSecretX25519(priv, pub): got success, want err") 120 } 121 } else { 122 if err != nil { 123 t.Errorf("ComputeSharedSecretX25519(priv, pub): got err %q, want nil", err) 124 } 125 if got, want := hex.EncodeToString(gotShared), test.Shared; got != want { 126 t.Errorf("ComputeSharedSecretX25519(shared): got %v, want %v", got, want) 127 } 128 } 129 }) 130 } 131 } 132} 133 134func TestComputeSharedSecretX25519Fails(t *testing.T) { 135 pubs := []string{ 136 // Should fail on non-32-byte inputs. 137 "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c", 138 "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a2a", 139 // Should fail on low order points, from Sodium 140 // https://github.com/jedisct1/libsodium/blob/65621a1059a37d/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c#L11-L70. 141 "0000000000000000000000000000000000000000000000000000000000000000", 142 "0100000000000000000000000000000000000000000000000000000000000000", 143 "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800", 144 "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157", 145 "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", 146 "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", 147 "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", 148 } 149 150 priv := make([]byte, curve25519.ScalarSize) 151 if _, err := rand.Read(priv); err != nil { 152 t.Fatal(err) 153 } 154 155 for i, pubHex := range pubs { 156 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 157 pub, err := hex.DecodeString(pubHex) 158 if err != nil { 159 t.Fatalf("DecodeString(pub): got err %q, want nil", err) 160 } 161 if _, err := subtle.ComputeSharedSecretX25519(priv, pub); err == nil { 162 t.Error("ComputeSharedSecretX25519(priv, pub): got success, want err") 163 } 164 }) 165 } 166} 167 168func TestPublicFromPrivateX25519WithRFCTestVectors(t *testing.T) { 169 // Test vectors are defined at 170 // https://datatracker.ietf.org/doc/html/rfc7748#section-6.1. 171 tests := []struct { 172 priv string 173 pub string 174 }{ 175 {"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a", "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"}, 176 {"5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb", "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"}, 177 } 178 179 for i, test := range tests { 180 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 181 priv, err := hex.DecodeString(test.priv) 182 if err != nil { 183 t.Fatalf("DecodeString(priv): got err %q, want nil", err) 184 } 185 gotPub, err := subtle.PublicFromPrivateX25519(priv) 186 if err != nil { 187 t.Fatalf("PublicFromPrivateX25519(priv): got err %q, want nil", err) 188 } 189 if got, want := hex.EncodeToString(gotPub), test.pub; got != want { 190 t.Errorf("PublicFromPrivateX25519(priv): got %s, want %s", got, want) 191 } 192 }) 193 } 194} 195 196func TestPublicFromPrivateX25519Fails(t *testing.T) { 197 // PublicFromPrivateX25519 fails on non-32-byte private keys. 198 privs := []string{ 199 "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c", 200 "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb95", 201 } 202 203 for i, priv := range privs { 204 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 205 priv, err := hex.DecodeString(priv) 206 if err != nil { 207 t.Fatalf("DecodeString(priv): got err %q, want nil", err) 208 } 209 if _, err := subtle.PublicFromPrivateX25519(priv); err == nil { 210 t.Error("PublicFromPrivateX25519(priv): got success, want err") 211 } 212 }) 213 } 214} 215