xref: /aosp_15_r20/external/tink/go/subtle/x25519_test.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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