diff --git a/src/pkg/Make.deps b/src/pkg/Make.deps index 5ef36f5e2a..614f48b668 100644 --- a/src/pkg/Make.deps +++ b/src/pkg/Make.deps @@ -17,6 +17,7 @@ crypto/hmac.install: crypto/md5.install crypto/sha1.install hash.install os.inst crypto/md5.install: hash.install os.install crypto/rc4.install: os.install strconv.install crypto/sha1.install: hash.install os.install +crypto/subtle.install: debug/dwarf.install: encoding/binary.install os.install strconv.install debug/macho.install: bytes.install debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install debug/elf.install: debug/dwarf.install encoding/binary.install fmt.install io.install os.install strconv.install diff --git a/src/pkg/Makefile b/src/pkg/Makefile index 61bd325db0..baf2122c88 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -31,6 +31,7 @@ DIRS=\ crypto/md5\ crypto/rc4\ crypto/sha1\ + crypto/subtle\ debug/dwarf\ debug/macho\ debug/elf\ diff --git a/src/pkg/crypto/rsa/pkcs1v15.go b/src/pkg/crypto/rsa/pkcs1v15.go index 9fb4584fe4..2583f19114 100644 --- a/src/pkg/crypto/rsa/pkcs1v15.go +++ b/src/pkg/crypto/rsa/pkcs1v15.go @@ -6,6 +6,7 @@ package rsa import ( "bytes"; + "crypto/subtle"; big "gmp"; "io"; "os"; @@ -27,7 +28,7 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, er // EM = 0x02 || PS || 0x00 || M em := make([]byte, k-1); em[0] = 2; - ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):len(em)]; + ps, mm := em[1 : len(em)-len(msg)-1], em[len(em)-len(msg) : len(em)]; err = nonZeroRandomBytes(ps, rand); if err != nil { return; @@ -77,8 +78,8 @@ func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []by return; } - valid &= constantTimeEq(int32(len(msg)), int32(len(key))); - constantTimeCopy(valid, key, msg); + valid &= subtle.ConstantTimeEq(int32(len(msg)), int32(len(key))); + subtle.ConstantTimeCopy(valid, key, msg); return; } @@ -96,8 +97,8 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid } em := leftPad(m.Bytes(), k); - firstByteIsZero := constantTimeByteEq(em[0], 0); - secondByteIsTwo := constantTimeByteEq(em[1], 2); + firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0); + secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2); // The remainder of the plaintext must be a string of non-zero random // octets, followed by a 0, followed by the message. @@ -107,9 +108,9 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid lookingForIndex = 1; for i := 2; i < len(em); i++ { - equals0 := constantTimeByteEq(em[i], 0); - index = constantTimeSelect(lookingForIndex & equals0, i, index); - lookingForIndex = constantTimeSelect(equals0, 0, lookingForIndex); + equals0 := subtle.ConstantTimeByteEq(em[i], 0); + index = subtle.ConstantTimeSelect(lookingForIndex & equals0, i, index); + lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex); } valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1); @@ -126,7 +127,7 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err os.Error) { for i := 0; i < len(s); i++ { for s[i] == 0 { - _, err = rand.Read(s[i:i+1]); + _, err = rand.Read(s[i : i+1]); if err != nil { return; } diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index adc9c8f021..8ca87485a8 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -8,11 +8,12 @@ package rsa // TODO(agl): Add support for PSS padding. import ( - "bytes"; - big "gmp"; - "hash"; - "io"; - "os"; + "bytes"; + "crypto/subtle"; + big "gmp"; + "hash"; + "io"; + "os"; ) var bigOne = big.NewInt(1) @@ -92,7 +93,7 @@ type PublicKey struct { // A PrivateKey represents an RSA key type PrivateKey struct { - PublicKey; // public part. + PublicKey; // public part. D *big.Int; // private exponent P, Q *big.Int; // prime factors of N } @@ -300,58 +301,6 @@ func modInverse(a, n *big.Int) (ia *big.Int) { return x; } -// constantTimeCompare returns 1 iff the two equal length slices, x -// and y, have equal contents. The time taken is a function of the length of -// the slices and is independent of the contents. -func constantTimeCompare(x, y []byte) int { - var v byte; - - for i := 0; i < len(x); i++ { - v |= x[i]^y[i]; - } - - return constantTimeByteEq(v, 0); -} - -// constantTimeSelect returns a if v is 1 and b if v is 0. -// Its behaviour is undefined if v takes any other value. -func constantTimeSelect(v, a, b int) int { - return ^(v-1)&a | (v-1)&b; -} - -// constantTimeByteEq returns 1 if a == b and 0 otherwise. -func constantTimeByteEq(a, b uint8) int { - x := ^(a^b); - x &= x>>4; - x &= x>>2; - x &= x>>1; - - return int(x); -} - -// constantTimeEq returns 1 if a == b and 0 otherwise. -func constantTimeEq(a, b int32) int { - x := ^(a^b); - x &= x>>16; - x &= x>>8; - x &= x>>4; - x &= x>>2; - x &= x>>1; - - return int(x&1); -} - -// constantTimeCopy copies the contents of y into x iff v == 1. If v == 0, x is left unchanged. -// Its behaviour is undefined if v takes any other value. -func constantTimeCopy(v int, x, y []byte) { - xmask := byte(v - 1); - ymask := byte(^(v - 1)); - for i := 0; i < len(x); i++ { - x[i] = x[i] & xmask | y[i] & ymask; - } - return; -} - // decrypt performs an RSA decryption, resulting in a plaintext integer. If a // random source is given, RSA blinding is used. func decrypt(rand io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err os.Error) { @@ -419,7 +368,7 @@ func DecryptOAEP(hash hash.Hash, rand io.Reader, priv *PrivateKey, ciphertext [] // anything about this.) em := leftPad(m.Bytes(), k); - firstByteIsZero := constantTimeByteEq(em[0], 0); + firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0); seed := em[1 : hash.Size() + 1]; db := em[hash.Size() + 1 : len(em)]; @@ -433,7 +382,7 @@ func DecryptOAEP(hash hash.Hash, rand io.Reader, priv *PrivateKey, ciphertext [] // attacks like: J. Manger. A Chosen Ciphertext Attack on RSA Optimal // Asymmetric Encryption Padding (OAEP) as Standardized in PKCS #1 // v2.0. In J. Kilian, editor, Advances in Cryptology. - lHash2Good := constantTimeCompare(lHash, lHash2); + lHash2Good := subtle.ConstantTimeCompare(lHash, lHash2); // The remainder of the plaintext must be zero or more 0x00, followed // by 0x01, followed by the message. @@ -445,11 +394,11 @@ func DecryptOAEP(hash hash.Hash, rand io.Reader, priv *PrivateKey, ciphertext [] rest := db[hash.Size() : len(db)]; for i := 0; i < len(rest); i++ { - equals0 := constantTimeByteEq(rest[i], 0); - equals1 := constantTimeByteEq(rest[i], 1); - index = constantTimeSelect(lookingForIndex & equals1, i, index); - lookingForIndex = constantTimeSelect(equals1, 0, lookingForIndex); - invalid = constantTimeSelect(lookingForIndex & ^equals0, 1, invalid); + equals0 := subtle.ConstantTimeByteEq(rest[i], 0); + equals1 := subtle.ConstantTimeByteEq(rest[i], 1); + index = subtle.ConstantTimeSelect(lookingForIndex & equals1, i, index); + lookingForIndex = subtle.ConstantTimeSelect(equals1, 0, lookingForIndex); + invalid = subtle.ConstantTimeSelect(lookingForIndex & ^equals0, 1, invalid); } if firstByteIsZero & lHash2Good & ^invalid & ^lookingForIndex != 1 { diff --git a/src/pkg/crypto/rsa/rsa_test.go b/src/pkg/crypto/rsa/rsa_test.go index df0d160dde..3bdfc66a79 100644 --- a/src/pkg/crypto/rsa/rsa_test.go +++ b/src/pkg/crypto/rsa/rsa_test.go @@ -5,12 +5,11 @@ package rsa import ( - "bytes"; - "crypto/sha1"; - big "gmp"; - "os"; - "testing"; - "testing/quick"; + "bytes"; + "crypto/sha1"; + big "gmp"; + "os"; + "testing"; ) func TestKeyGeneration(t *testing.T) { @@ -109,101 +108,6 @@ func TestDecryptOAEP(t *testing.T) { } } -type TestConstantTimeCompareStruct struct { - a, b []byte; - out int; -} - -var testConstandTimeCompareData = []TestConstantTimeCompareStruct{ - TestConstantTimeCompareStruct{[]byte{}, []byte{}, 1}, - TestConstantTimeCompareStruct{[]byte{0x11}, []byte{0x11}, 1}, - TestConstantTimeCompareStruct{[]byte{0x12}, []byte{0x11}, 0}, -} - -func TestConstantTimeCompare(t *testing.T) { - for i, test := range testConstandTimeCompareData { - if r := constantTimeCompare(test.a, test.b); r != test.out { - t.Errorf("#%d bad result (got %x, want %x)", i, r, test.out); - } - } -} - -type TestConstantTimeByteEqStruct struct { - a, b uint8; - out int; -} - -var testConstandTimeByteEqData = []TestConstantTimeByteEqStruct{ - TestConstantTimeByteEqStruct{0, 0, 1}, - TestConstantTimeByteEqStruct{0, 1, 0}, - TestConstantTimeByteEqStruct{1, 0, 0}, - TestConstantTimeByteEqStruct{0xff, 0xff, 1}, - TestConstantTimeByteEqStruct{0xff, 0xfe, 0}, -} - -func ByteEq(a, b uint8) int { - if a == b { - return 1; - } - return 0; -} - -func TestConstantTimeByteEq(t *testing.T) { - for i, test := range testConstandTimeByteEqData { - if r := constantTimeByteEq(test.a, test.b); r != test.out { - t.Errorf("#%d bad result (got %x, want %x)", i, r, test.out); - } - } - err := quick.CheckEqual(constantTimeByteEq, ByteEq, nil); - if err != nil { - t.Error(err); - } -} - -func Eq(a, b int32) int { - if a == b { - return 1; - } - return 0; -} - -func TestConstantTimeEq(t *testing.T) { - err := quick.CheckEqual(constantTimeEq, Eq, nil); - if err != nil { - t.Error(err); - } -} - -func Copy(v int, x, y []byte) []byte { - if len(x) > len(y) { - x = x[0:len(y)]; - } else { - y = y[0:len(x)]; - } - if v == 1 { - bytes.Copy(x, y); - } - return x; -} - -func constantTimeCopyWrapper(v int, x, y []byte) []byte { - if len(x) > len(y) { - x = x[0:len(y)]; - } else { - y = y[0:len(x)]; - } - v &= 1; - constantTimeCopy(v, x, y); - return x; -} - -func TestConstantTimeCopy(t *testing.T) { - err := quick.CheckEqual(constantTimeCopyWrapper, Copy, nil); - if err != nil { - t.Error(err); - } -} - // testEncryptOAEPData contains a subset of the vectors from RSA's "Test vectors for RSA-OAEP". var testEncryptOAEPData = []testEncryptOAEPStruct{ // Key 1 @@ -305,47 +209,48 @@ var testEncryptOAEPData = []testEncryptOAEPStruct{ 65537, "056b04216fe5f354ac77250a4b6b0c8525a85c59b0bd80c56450a22d5f438e596a333aa875e291dd43f48cb88b9d5fc0d499f9fcd1c397f9afc070cd9e398c8d19e61db7c7410a6b2675dfbf5d345b804d201add502d5ce2dfcb091ce9997bbebe57306f383e4d588103f036f7e85d1934d152a323e4a8db451d6f4a5b1b0f102cc150e02feee2b88dea4ad4c1baccb24d84072d14e1d24a6771f7408ee30564fb86d4393a34bcf0b788501d193303f13a2284b001f0f649eaf79328d4ac5c430ab4414920a9460ed1b7bc40ec653e876d09abc509ae45b525190116a0c26101848298509c1c3bf3a483e7274054e15e97075036e989f60932807b5257751e79", []testEncryptOAEPMessage{ - // Example 10.1 - testEncryptOAEPMessage{ - []byte{0x8b, 0xba, 0x6b, 0xf8, 0x2a, 0x6c, 0x0f, 0x86, - 0xd5, 0xf1, 0x75, 0x6e, 0x97, 0x95, 0x68, 0x70, 0xb0, - 0x89, 0x53, 0xb0, 0x6b, 0x4e, 0xb2, 0x05, 0xbc, 0x16, - 0x94, 0xee, + // Example 10.1 + testEncryptOAEPMessage{ + []byte{0x8b, 0xba, 0x6b, 0xf8, 0x2a, 0x6c, 0x0f, 0x86, + 0xd5, 0xf1, 0x75, 0x6e, 0x97, 0x95, 0x68, 0x70, 0xb0, + 0x89, 0x53, 0xb0, 0x6b, 0x4e, 0xb2, 0x05, 0xbc, 0x16, + 0x94, 0xee, + }, + []byte{0x47, 0xe1, 0xab, 0x71, 0x19, 0xfe, 0xe5, 0x6c, + 0x95, 0xee, 0x5e, 0xaa, 0xd8, 0x6f, 0x40, 0xd0, 0xaa, + 0x63, 0xbd, 0x33, + }, + []byte{0x53, 0xea, 0x5d, 0xc0, 0x8c, 0xd2, 0x60, 0xfb, + 0x3b, 0x85, 0x85, 0x67, 0x28, 0x7f, 0xa9, 0x15, 0x52, + 0xc3, 0x0b, 0x2f, 0xeb, 0xfb, 0xa2, 0x13, 0xf0, 0xae, + 0x87, 0x70, 0x2d, 0x06, 0x8d, 0x19, 0xba, 0xb0, 0x7f, + 0xe5, 0x74, 0x52, 0x3d, 0xfb, 0x42, 0x13, 0x9d, 0x68, + 0xc3, 0xc5, 0xaf, 0xee, 0xe0, 0xbf, 0xe4, 0xcb, 0x79, + 0x69, 0xcb, 0xf3, 0x82, 0xb8, 0x04, 0xd6, 0xe6, 0x13, + 0x96, 0x14, 0x4e, 0x2d, 0x0e, 0x60, 0x74, 0x1f, 0x89, + 0x93, 0xc3, 0x01, 0x4b, 0x58, 0xb9, 0xb1, 0x95, 0x7a, + 0x8b, 0xab, 0xcd, 0x23, 0xaf, 0x85, 0x4f, 0x4c, 0x35, + 0x6f, 0xb1, 0x66, 0x2a, 0xa7, 0x2b, 0xfc, 0xc7, 0xe5, + 0x86, 0x55, 0x9d, 0xc4, 0x28, 0x0d, 0x16, 0x0c, 0x12, + 0x67, 0x85, 0xa7, 0x23, 0xeb, 0xee, 0xbe, 0xff, 0x71, + 0xf1, 0x15, 0x94, 0x44, 0x0a, 0xae, 0xf8, 0x7d, 0x10, + 0x79, 0x3a, 0x87, 0x74, 0xa2, 0x39, 0xd4, 0xa0, 0x4c, + 0x87, 0xfe, 0x14, 0x67, 0xb9, 0xda, 0xf8, 0x52, 0x08, + 0xec, 0x6c, 0x72, 0x55, 0x79, 0x4a, 0x96, 0xcc, 0x29, + 0x14, 0x2f, 0x9a, 0x8b, 0xd4, 0x18, 0xe3, 0xc1, 0xfd, + 0x67, 0x34, 0x4b, 0x0c, 0xd0, 0x82, 0x9d, 0xf3, 0xb2, + 0xbe, 0xc6, 0x02, 0x53, 0x19, 0x62, 0x93, 0xc6, 0xb3, + 0x4d, 0x3f, 0x75, 0xd3, 0x2f, 0x21, 0x3d, 0xd4, 0x5c, + 0x62, 0x73, 0xd5, 0x05, 0xad, 0xf4, 0xcc, 0xed, 0x10, + 0x57, 0xcb, 0x75, 0x8f, 0xc2, 0x6a, 0xee, 0xfa, 0x44, + 0x12, 0x55, 0xed, 0x4e, 0x64, 0xc1, 0x99, 0xee, 0x07, + 0x5e, 0x7f, 0x16, 0x64, 0x61, 0x82, 0xfd, 0xb4, 0x64, + 0x73, 0x9b, 0x68, 0xab, 0x5d, 0xaf, 0xf0, 0xe6, 0x3e, + 0x95, 0x52, 0x01, 0x68, 0x24, 0xf0, 0x54, 0xbf, 0x4d, + 0x3c, 0x8c, 0x90, 0xa9, 0x7b, 0xb6, 0xb6, 0x55, 0x32, + 0x84, 0xeb, 0x42, 0x9f, 0xcc, + }, }, - []byte{0x47, 0xe1, 0xab, 0x71, 0x19, 0xfe, 0xe5, 0x6c, - 0x95, 0xee, 0x5e, 0xaa, 0xd8, 0x6f, 0x40, 0xd0, 0xaa, - 0x63, 0xbd, 0x33, - }, - []byte{0x53, 0xea, 0x5d, 0xc0, 0x8c, 0xd2, 0x60, 0xfb, - 0x3b, 0x85, 0x85, 0x67, 0x28, 0x7f, 0xa9, 0x15, 0x52, - 0xc3, 0x0b, 0x2f, 0xeb, 0xfb, 0xa2, 0x13, 0xf0, 0xae, - 0x87, 0x70, 0x2d, 0x06, 0x8d, 0x19, 0xba, 0xb0, 0x7f, - 0xe5, 0x74, 0x52, 0x3d, 0xfb, 0x42, 0x13, 0x9d, 0x68, - 0xc3, 0xc5, 0xaf, 0xee, 0xe0, 0xbf, 0xe4, 0xcb, 0x79, - 0x69, 0xcb, 0xf3, 0x82, 0xb8, 0x04, 0xd6, 0xe6, 0x13, - 0x96, 0x14, 0x4e, 0x2d, 0x0e, 0x60, 0x74, 0x1f, 0x89, - 0x93, 0xc3, 0x01, 0x4b, 0x58, 0xb9, 0xb1, 0x95, 0x7a, - 0x8b, 0xab, 0xcd, 0x23, 0xaf, 0x85, 0x4f, 0x4c, 0x35, - 0x6f, 0xb1, 0x66, 0x2a, 0xa7, 0x2b, 0xfc, 0xc7, 0xe5, - 0x86, 0x55, 0x9d, 0xc4, 0x28, 0x0d, 0x16, 0x0c, 0x12, - 0x67, 0x85, 0xa7, 0x23, 0xeb, 0xee, 0xbe, 0xff, 0x71, - 0xf1, 0x15, 0x94, 0x44, 0x0a, 0xae, 0xf8, 0x7d, 0x10, - 0x79, 0x3a, 0x87, 0x74, 0xa2, 0x39, 0xd4, 0xa0, 0x4c, - 0x87, 0xfe, 0x14, 0x67, 0xb9, 0xda, 0xf8, 0x52, 0x08, - 0xec, 0x6c, 0x72, 0x55, 0x79, 0x4a, 0x96, 0xcc, 0x29, - 0x14, 0x2f, 0x9a, 0x8b, 0xd4, 0x18, 0xe3, 0xc1, 0xfd, - 0x67, 0x34, 0x4b, 0x0c, 0xd0, 0x82, 0x9d, 0xf3, 0xb2, - 0xbe, 0xc6, 0x02, 0x53, 0x19, 0x62, 0x93, 0xc6, 0xb3, - 0x4d, 0x3f, 0x75, 0xd3, 0x2f, 0x21, 0x3d, 0xd4, 0x5c, - 0x62, 0x73, 0xd5, 0x05, 0xad, 0xf4, 0xcc, 0xed, 0x10, - 0x57, 0xcb, 0x75, 0x8f, 0xc2, 0x6a, 0xee, 0xfa, 0x44, - 0x12, 0x55, 0xed, 0x4e, 0x64, 0xc1, 0x99, 0xee, 0x07, - 0x5e, 0x7f, 0x16, 0x64, 0x61, 0x82, 0xfd, 0xb4, 0x64, - 0x73, 0x9b, 0x68, 0xab, 0x5d, 0xaf, 0xf0, 0xe6, 0x3e, - 0x95, 0x52, 0x01, 0x68, 0x24, 0xf0, 0x54, 0xbf, 0x4d, - 0x3c, 0x8c, 0x90, 0xa9, 0x7b, 0xb6, 0xb6, 0x55, 0x32, - 0x84, 0xeb, 0x42, 0x9f, 0xcc, - }, - }}, + }, }, } diff --git a/src/pkg/crypto/subtle/Makefile b/src/pkg/crypto/subtle/Makefile new file mode 100644 index 0000000000..4d245c6839 --- /dev/null +++ b/src/pkg/crypto/subtle/Makefile @@ -0,0 +1,11 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include $(GOROOT)/src/Make.$(GOARCH) + +TARG=crypto/subtle +GOFILES=\ + constant_time.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/crypto/subtle/constant_time.go b/src/pkg/crypto/subtle/constant_time.go new file mode 100644 index 0000000000..a1d2eaf998 --- /dev/null +++ b/src/pkg/crypto/subtle/constant_time.go @@ -0,0 +1,59 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This package implements functions that are often useful in cryptographic +// code but require careful thought to use correctly. +package subtle + +// ConstantTimeCompare returns 1 iff the two equal length slices, x +// and y, have equal contents. The time taken is a function of the length of +// the slices and is independent of the contents. +func ConstantTimeCompare(x, y []byte) int { + var v byte; + + for i := 0; i < len(x); i++ { + v |= x[i]^y[i]; + } + + return ConstantTimeByteEq(v, 0); +} + +// ConstantTimeSelect returns x if v is 1 and y if v is 0. +// Its behavior is undefined if v takes any other value. +func ConstantTimeSelect(v, x, y int) int { + return ^(v-1) & x | (v-1)&y; +} + +// ConstantTimeByteEq returns 1 if x == x and 0 otherwise. +func ConstantTimeByteEq(x, y uint8) int { + z := ^(x^y); + z &= z>>4; + z &= z>>2; + z &= z>>1; + + return int(z); +} + +// ConstantTimeEq returns 1 if x == y and 0 otherwise. +func ConstantTimeEq(x, y int32) int { + z := ^(x^y); + z &= z>>16; + z &= z>>8; + z &= z>>4; + z &= z>>2; + z &= z>>1; + + return int(z&1); +} + +// ConstantTimeCopy copies the contents of y into x iff v == 1. If v == 0, x is left unchanged. +// Its behavior is undefined if v takes any other value. +func ConstantTimeCopy(v int, x, y []byte) { + xmask := byte(v-1); + ymask := byte(^(v-1)); + for i := 0; i < len(x); i++ { + x[i] = x[i]&xmask | y[i]&ymask; + } + return; +} diff --git a/src/pkg/crypto/subtle/constant_time_test.go b/src/pkg/crypto/subtle/constant_time_test.go new file mode 100644 index 0000000000..78aa6771d2 --- /dev/null +++ b/src/pkg/crypto/subtle/constant_time_test.go @@ -0,0 +1,106 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package subtle + +import ( + "bytes"; + "testing"; + "testing/quick"; +) + +type TestConstantTimeCompareStruct struct { + a, b []byte; + out int; +} + +var testConstandTimeCompareData = []TestConstantTimeCompareStruct{ + TestConstantTimeCompareStruct{[]byte{}, []byte{}, 1}, + TestConstantTimeCompareStruct{[]byte{0x11}, []byte{0x11}, 1}, + TestConstantTimeCompareStruct{[]byte{0x12}, []byte{0x11}, 0}, +} + +func TestConstantTimeCompare(t *testing.T) { + for i, test := range testConstandTimeCompareData { + if r := ConstantTimeCompare(test.a, test.b); r != test.out { + t.Errorf("#%d bad result (got %x, want %x)", i, r, test.out); + } + } +} + +type TestConstantTimeByteEqStruct struct { + a, b uint8; + out int; +} + +var testConstandTimeByteEqData = []TestConstantTimeByteEqStruct{ + TestConstantTimeByteEqStruct{0, 0, 1}, + TestConstantTimeByteEqStruct{0, 1, 0}, + TestConstantTimeByteEqStruct{1, 0, 0}, + TestConstantTimeByteEqStruct{0xff, 0xff, 1}, + TestConstantTimeByteEqStruct{0xff, 0xfe, 0}, +} + +func byteEq(a, b uint8) int { + if a == b { + return 1; + } + return 0; +} + +func TestConstantTimeByteEq(t *testing.T) { + for i, test := range testConstandTimeByteEqData { + if r := ConstantTimeByteEq(test.a, test.b); r != test.out { + t.Errorf("#%d bad result (got %x, want %x)", i, r, test.out); + } + } + err := quick.CheckEqual(ConstantTimeByteEq, byteEq, nil); + if err != nil { + t.Error(err); + } +} + +func eq(a, b int32) int { + if a == b { + return 1; + } + return 0; +} + +func TestConstantTimeEq(t *testing.T) { + err := quick.CheckEqual(ConstantTimeEq, eq, nil); + if err != nil { + t.Error(err); + } +} + +func copy(v int, x, y []byte) []byte { + if len(x) > len(y) { + x = x[0:len(y)]; + } else { + y = y[0:len(x)]; + } + if v == 1 { + bytes.Copy(x, y); + } + return x; +} + +func constantTimeCopyWrapper(v int, x, y []byte) []byte { + if len(x) > len(y) { + x = x[0:len(y)]; + } else { + y = y[0:len(x)]; + } + v &= 1; + ConstantTimeCopy(v, x, y); + return x; +} + +func TestConstantTimeCopy(t *testing.T) { + err := quick.CheckEqual(constantTimeCopyWrapper, copy, nil); + if err != nil { + t.Error(err); + } +}