Commit d8f41257 by Ian Lance Taylor

Update Go library to last weekly.

From-SVN: r180552
parent e0c39d66
...@@ -13,7 +13,6 @@ package main ...@@ -13,7 +13,6 @@ package main
import ( import (
"container/heap" "container/heap"
"container/ring" "container/ring"
"container/vector"
) )
// Return a chan of odd numbers, starting from 5. // Return a chan of odd numbers, starting from 5.
...@@ -47,13 +46,28 @@ type PeekCh struct { ...@@ -47,13 +46,28 @@ type PeekCh struct {
ch chan int ch chan int
} }
// Heap of PeekCh, sorting by head values. // Heap of PeekCh, sorting by head values, satisfies Heap interface.
type PeekChHeap struct { type PeekChHeap []*PeekCh
*vector.Vector
}
func (h *PeekChHeap) Less(i, j int) bool { func (h *PeekChHeap) Less(i, j int) bool {
return h.At(i).(*PeekCh).head < h.At(j).(*PeekCh).head return (*h)[i].head < (*h)[j].head
}
func (h *PeekChHeap) Swap(i, j int) {
(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
}
func (h *PeekChHeap) Len() int {
return len(*h)
}
func (h *PeekChHeap) Pop() (v interface{}) {
*h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1]
return
}
func (h *PeekChHeap) Push(v interface{}) {
*h = append(*h, v.(*PeekCh))
} }
// Return a channel to serve as a sending proxy to 'out'. // Return a channel to serve as a sending proxy to 'out'.
...@@ -108,26 +122,26 @@ func Sieve() chan int { ...@@ -108,26 +122,26 @@ func Sieve() chan int {
// Merge channels of multiples of 'primes' into 'composites'. // Merge channels of multiples of 'primes' into 'composites'.
go func() { go func() {
h := &PeekChHeap{new(vector.Vector)} var h PeekChHeap
min := 15 min := 15
for { for {
m := multiples(<-primes) m := multiples(<-primes)
head := <-m head := <-m
for min < head { for min < head {
composites <- min composites <- min
minchan := heap.Pop(h).(*PeekCh) minchan := heap.Pop(&h).(*PeekCh)
min = minchan.head min = minchan.head
minchan.head = <-minchan.ch minchan.head = <-minchan.ch
heap.Push(h, minchan) heap.Push(&h, minchan)
} }
for min == head { for min == head {
minchan := heap.Pop(h).(*PeekCh) minchan := heap.Pop(&h).(*PeekCh)
min = minchan.head min = minchan.head
minchan.head = <-minchan.ch minchan.head = <-minchan.ch
heap.Push(h, minchan) heap.Push(&h, minchan)
} }
composites <- head composites <- head
heap.Push(h, &PeekCh{<-m, m}) heap.Push(&h, &PeekCh{<-m, m})
} }
}() }()
......
// $G $F.go && $L $F.$A && ./$A.out
// 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 main
import "container/vector"
type S struct {
val int
}
func (p *S) Init(val int) *S {
p.val = val
return p
}
func test0() {
v := new(vector.Vector)
if v.Len() != 0 {
print("len = ", v.Len(), "\n")
panic("fail")
}
}
func test1() {
var a [1000]*S
for i := 0; i < len(a); i++ {
a[i] = new(S).Init(i)
}
v := new(vector.Vector)
for i := 0; i < len(a); i++ {
v.Insert(0, a[i])
if v.Len() != i+1 {
print("len = ", v.Len(), "\n")
panic("fail")
}
}
for i := 0; i < v.Len(); i++ {
x := v.At(i).(*S)
if x.val != v.Len()-i-1 {
print("expected ", i, ", found ", x.val, "\n")
panic("fail")
}
}
for v.Len() > 10 {
v.Delete(10)
}
}
func main() {
test0()
test1()
}
c1702f36df03 6d7136d74b65
The first line of this file holds the Mercurial revision number of the The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources. last merge done from the master library sources.
...@@ -15,36 +15,37 @@ const ( ...@@ -15,36 +15,37 @@ const (
blockSize = 512 blockSize = 512
// Types // Types
TypeReg = '0' TypeReg = '0' // regular file.
TypeRegA = '\x00' TypeRegA = '\x00' // regular file.
TypeLink = '1' TypeLink = '1' // hard link.
TypeSymlink = '2' TypeSymlink = '2' // symbolic link.
TypeChar = '3' TypeChar = '3' // character device node.
TypeBlock = '4' TypeBlock = '4' // block device node.
TypeDir = '5' TypeDir = '5' // directory.
TypeFifo = '6' TypeFifo = '6' // fifo node.
TypeCont = '7' TypeCont = '7' // reserved.
TypeXHeader = 'x' TypeXHeader = 'x' // extended header.
TypeXGlobalHeader = 'g' TypeXGlobalHeader = 'g' // global extended header.
) )
// A Header represents a single header in a tar archive. // A Header represents a single header in a tar archive.
// Some fields may not be populated. // Some fields may not be populated.
type Header struct { type Header struct {
Name string Name string // name of header file entry.
Mode int64 Mode int64 // permission and mode bits.
Uid int Uid int // user id of owner.
Gid int Gid int // group id of owner.
Size int64 Size int64 // length in bytes.
Mtime int64 Mtime int64 // modified time; seconds since epoch.
Typeflag byte Typeflag byte // type of header entry.
Linkname string Linkname string // target name of link.
Uname string Uname string // user name of owner.
Gname string Gname string // group name of owner.
Devmajor int64 Devmajor int64 // major number of character or block device.
Devminor int64 Devminor int64 // minor number of character or block device.
Atime int64 Atime int64 // access time; seconds since epoch.
Ctime int64 Ctime int64 // status change time; seconds since epoch.
} }
var zeroBlock = make([]byte, blockSize) var zeroBlock = make([]byte, blockSize)
......
...@@ -94,7 +94,7 @@ func (tr *Reader) skipUnread() { ...@@ -94,7 +94,7 @@ func (tr *Reader) skipUnread() {
return return
} }
} }
_, tr.err = io.Copyn(ioutil.Discard, tr.r, nr) _, tr.err = io.CopyN(ioutil.Discard, tr.r, nr)
} }
func (tr *Reader) verifyChecksum(header []byte) bool { func (tr *Reader) verifyChecksum(header []byte) bool {
......
...@@ -134,7 +134,7 @@ func (tw *Writer) WriteHeader(hdr *Header) os.Error { ...@@ -134,7 +134,7 @@ func (tw *Writer) WriteHeader(hdr *Header) os.Error {
tw.numeric(s.next(12), hdr.Mtime) // 136:148 tw.numeric(s.next(12), hdr.Mtime) // 136:148
s.next(8) // chksum (148:156) s.next(8) // chksum (148:156)
s.next(1)[0] = hdr.Typeflag // 156:157 s.next(1)[0] = hdr.Typeflag // 156:157
s.next(100) // linkname (157:257) tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
copy(s.next(8), []byte("ustar\x0000")) // 257:265 copy(s.next(8), []byte("ustar\x0000")) // 257:265
tw.cString(s.next(32), hdr.Uname) // 265:297 tw.cString(s.next(32), hdr.Uname) // 265:297
tw.cString(s.next(32), hdr.Gname) // 297:329 tw.cString(s.next(32), hdr.Gname) // 297:329
......
...@@ -24,6 +24,10 @@ type writerTest struct { ...@@ -24,6 +24,10 @@ type writerTest struct {
} }
var writerTests = []*writerTest{ var writerTests = []*writerTest{
// The writer test file was produced with this command:
// tar (GNU tar) 1.26
// ln -s small.txt link.txt
// tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
&writerTest{ &writerTest{
file: "testdata/writer.tar", file: "testdata/writer.tar",
entries: []*writerTestEntry{ entries: []*writerTestEntry{
...@@ -55,6 +59,21 @@ var writerTests = []*writerTest{ ...@@ -55,6 +59,21 @@ var writerTests = []*writerTest{
}, },
contents: "Google.com\n", contents: "Google.com\n",
}, },
&writerTestEntry{
header: &Header{
Name: "link.txt",
Mode: 0777,
Uid: 1000,
Gid: 1000,
Size: 0,
Mtime: 1314603082,
Typeflag: '2',
Linkname: "small.txt",
Uname: "strings",
Gname: "strings",
},
// no contents
},
}, },
}, },
// The truncated test file was produced using these commands: // The truncated test file was produced using these commands:
......
...@@ -238,7 +238,7 @@ func readDirectoryHeader(f *File, r io.Reader) os.Error { ...@@ -238,7 +238,7 @@ func readDirectoryHeader(f *File, r io.Reader) os.Error {
commentLen := int(c.Uint16(b[32:34])) commentLen := int(c.Uint16(b[32:34]))
// startDiskNumber := c.Uint16(b[34:36]) // Unused // startDiskNumber := c.Uint16(b[34:36]) // Unused
// internalAttributes := c.Uint16(b[36:38]) // Unused // internalAttributes := c.Uint16(b[36:38]) // Unused
// externalAttributes := c.Uint32(b[38:42]) // Unused f.ExternalAttrs = c.Uint32(b[38:42])
f.headerOffset = int64(c.Uint32(b[42:46])) f.headerOffset = int64(c.Uint32(b[42:46]))
d := make([]byte, filenameLen+extraLen+commentLen) d := make([]byte, filenameLen+extraLen+commentLen)
if _, err := io.ReadFull(r, d); err != nil { if _, err := io.ReadFull(r, d); err != nil {
......
...@@ -26,6 +26,7 @@ type ZipTestFile struct { ...@@ -26,6 +26,7 @@ type ZipTestFile struct {
Content []byte // if blank, will attempt to compare against File Content []byte // if blank, will attempt to compare against File
File string // name of file to compare to (relative to testdata/) File string // name of file to compare to (relative to testdata/)
Mtime string // modified time in format "mm-dd-yy hh:mm:ss" Mtime string // modified time in format "mm-dd-yy hh:mm:ss"
Mode uint32
} }
// Caution: The Mtime values found for the test files should correspond to // Caution: The Mtime values found for the test files should correspond to
...@@ -47,11 +48,13 @@ var tests = []ZipTest{ ...@@ -47,11 +48,13 @@ var tests = []ZipTest{
Name: "test.txt", Name: "test.txt",
Content: []byte("This is a test text file.\n"), Content: []byte("This is a test text file.\n"),
Mtime: "09-05-10 12:12:02", Mtime: "09-05-10 12:12:02",
Mode: 0x81a4,
}, },
{ {
Name: "gophercolor16x16.png", Name: "gophercolor16x16.png",
File: "gophercolor16x16.png", File: "gophercolor16x16.png",
Mtime: "09-05-10 15:52:58", Mtime: "09-05-10 15:52:58",
Mode: 0x81a4,
}, },
}, },
}, },
...@@ -162,6 +165,8 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { ...@@ -162,6 +165,8 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want) t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want)
} }
testFileMode(t, f, ft.Mode)
size0 := f.UncompressedSize size0 := f.UncompressedSize
var b bytes.Buffer var b bytes.Buffer
...@@ -203,6 +208,19 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) { ...@@ -203,6 +208,19 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
} }
} }
func testFileMode(t *testing.T, f *File, want uint32) {
mode, err := f.Mode()
if want == 0 {
if err == nil {
t.Errorf("%s mode: got %v, want none", f.Name, mode)
}
} else if err != nil {
t.Errorf("%s mode: %s", f.Name, err)
} else if mode != want {
t.Errorf("%s mode: want 0x%x, got 0x%x", f.Name, want, mode)
}
}
func TestInvalidFiles(t *testing.T) { func TestInvalidFiles(t *testing.T) {
const size = 1024 * 70 // 70kb const size = 1024 * 70 // 70kb
b := make([]byte, size) b := make([]byte, size)
......
...@@ -28,6 +28,9 @@ const ( ...@@ -28,6 +28,9 @@ const (
directoryHeaderLen = 46 // + filename + extra + comment directoryHeaderLen = 46 // + filename + extra + comment
directoryEndLen = 22 // + comment directoryEndLen = 22 // + comment
dataDescriptorLen = 12 dataDescriptorLen = 12
// Constants for the first byte in CreatorVersion
creatorUnix = 3
) )
type FileHeader struct { type FileHeader struct {
...@@ -42,6 +45,7 @@ type FileHeader struct { ...@@ -42,6 +45,7 @@ type FileHeader struct {
CompressedSize uint32 CompressedSize uint32
UncompressedSize uint32 UncompressedSize uint32
Extra []byte Extra []byte
ExternalAttrs uint32 // Meaning depends on CreatorVersion
Comment string Comment string
} }
...@@ -89,3 +93,18 @@ func (h *FileHeader) Mtime_ns() int64 { ...@@ -89,3 +93,18 @@ func (h *FileHeader) Mtime_ns() int64 {
t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime) t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
return t.Seconds() * 1e9 return t.Seconds() * 1e9
} }
// Mode returns the permission and mode bits for the FileHeader.
// An error is returned in case the information is not available.
func (h *FileHeader) Mode() (mode uint32, err os.Error) {
if h.CreatorVersion>>8 == creatorUnix {
return h.ExternalAttrs >> 16, nil
}
return 0, os.NewError("file mode not available")
}
// SetMode changes the permission and mode bits for the FileHeader.
func (h *FileHeader) SetMode(mode uint32) {
h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
h.ExternalAttrs = mode << 16
}
...@@ -69,7 +69,7 @@ func (w *Writer) Close() (err os.Error) { ...@@ -69,7 +69,7 @@ func (w *Writer) Close() (err os.Error) {
write(w, uint16(len(h.Comment))) write(w, uint16(len(h.Comment)))
write(w, uint16(0)) // disk number start write(w, uint16(0)) // disk number start
write(w, uint16(0)) // internal file attributes write(w, uint16(0)) // internal file attributes
write(w, uint32(0)) // external file attributes write(w, h.ExternalAttrs)
write(w, h.offset) write(w, h.offset)
writeBytes(w, []byte(h.Name)) writeBytes(w, []byte(h.Name))
writeBytes(w, h.Extra) writeBytes(w, h.Extra)
...@@ -115,7 +115,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) { ...@@ -115,7 +115,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) {
} }
fh.Flags |= 0x8 // we will write a data descriptor fh.Flags |= 0x8 // we will write a data descriptor
fh.CreatorVersion = 0x14 fh.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14
fh.ReaderVersion = 0x14 fh.ReaderVersion = 0x14
fw := &fileWriter{ fw := &fileWriter{
......
...@@ -13,19 +13,45 @@ import ( ...@@ -13,19 +13,45 @@ import (
// TODO(adg): a more sophisticated test suite // TODO(adg): a more sophisticated test suite
const testString = "Rabbits, guinea pigs, gophers, marsupial rats, and quolls." type WriteTest struct {
Name string
Data []byte
Method uint16
Mode uint32
}
var writeTests = []WriteTest{
WriteTest{
Name: "foo",
Data: []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),
Method: Store,
},
WriteTest{
Name: "bar",
Data: nil, // large data set in the test
Method: Deflate,
Mode: 0x81ed,
},
}
func TestWriter(t *testing.T) { func TestWriter(t *testing.T) {
largeData := make([]byte, 1<<17) largeData := make([]byte, 1<<17)
for i := range largeData { for i := range largeData {
largeData[i] = byte(rand.Int()) largeData[i] = byte(rand.Int())
} }
writeTests[1].Data = largeData
defer func() {
writeTests[1].Data = nil
}()
// write a zip file // write a zip file
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
w := NewWriter(buf) w := NewWriter(buf)
testCreate(t, w, "foo", []byte(testString), Store)
testCreate(t, w, "bar", largeData, Deflate) for _, wt := range writeTests {
testCreate(t, w, &wt)
}
if err := w.Close(); err != nil { if err := w.Close(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
...@@ -35,26 +61,34 @@ func TestWriter(t *testing.T) { ...@@ -35,26 +61,34 @@ func TestWriter(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
testReadFile(t, r.File[0], []byte(testString)) for i, wt := range writeTests {
testReadFile(t, r.File[1], largeData) testReadFile(t, r.File[i], &wt)
}
} }
func testCreate(t *testing.T, w *Writer, name string, data []byte, method uint16) { func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
header := &FileHeader{ header := &FileHeader{
Name: name, Name: wt.Name,
Method: method, Method: wt.Method,
}
if wt.Mode != 0 {
header.SetMode(wt.Mode)
} }
f, err := w.CreateHeader(header) f, err := w.CreateHeader(header)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
_, err = f.Write(data) _, err = f.Write(wt.Data)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
func testReadFile(t *testing.T, f *File, data []byte) { func testReadFile(t *testing.T, f *File, wt *WriteTest) {
if f.Name != wt.Name {
t.Fatalf("File name: got %q, want %q", f.Name, wt.Name)
}
testFileMode(t, f, wt.Mode)
rc, err := f.Open() rc, err := f.Open()
if err != nil { if err != nil {
t.Fatal("opening:", err) t.Fatal("opening:", err)
...@@ -67,7 +101,7 @@ func testReadFile(t *testing.T, f *File, data []byte) { ...@@ -67,7 +101,7 @@ func testReadFile(t *testing.T, f *File, data []byte) {
if err != nil { if err != nil {
t.Fatal("closing:", err) t.Fatal("closing:", err)
} }
if !bytes.Equal(b, data) { if !bytes.Equal(b, wt.Data) {
t.Errorf("File contents %q, want %q", b, data) t.Errorf("File contents %q, want %q", b, wt.Data)
} }
} }
...@@ -516,6 +516,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam ...@@ -516,6 +516,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
result, err = parseIA5String(innerBytes) result, err = parseIA5String(innerBytes)
case tagT61String: case tagT61String:
result, err = parseT61String(innerBytes) result, err = parseT61String(innerBytes)
case tagUTF8String:
result, err = parseUTF8String(innerBytes)
case tagInteger: case tagInteger:
result, err = parseInt64(innerBytes) result, err = parseInt64(innerBytes)
case tagBitString: case tagBitString:
......
...@@ -206,10 +206,10 @@ type timeTest struct { ...@@ -206,10 +206,10 @@ type timeTest struct {
} }
var utcTestData = []timeTest{ var utcTestData = []timeTest{
{"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, -7 * 60 * 60, ""}}, {"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}},
{"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, 7*60*60 + 30*60, ""}}, {"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}},
{"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, 0, "UTC"}}, {"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, "UTC"}},
{"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, 0, "UTC"}}, {"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, "UTC"}},
{"a10506234540Z", false, nil}, {"a10506234540Z", false, nil},
{"91a506234540Z", false, nil}, {"91a506234540Z", false, nil},
{"9105a6234540Z", false, nil}, {"9105a6234540Z", false, nil},
...@@ -235,10 +235,10 @@ func TestUTCTime(t *testing.T) { ...@@ -235,10 +235,10 @@ func TestUTCTime(t *testing.T) {
} }
var generalizedTimeTestData = []timeTest{ var generalizedTimeTestData = []timeTest{
{"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 0, "UTC"}}, {"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, "UTC"}},
{"20100102030405", false, nil}, {"20100102030405", false, nil},
{"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 6*60*60 + 7*60, ""}}, {"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 6*60*60 + 7*60, ""}},
{"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, -6*60*60 - 7*60, ""}}, {"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, -6*60*60 - 7*60, ""}},
} }
func TestGeneralizedTime(t *testing.T) { func TestGeneralizedTime(t *testing.T) {
...@@ -475,7 +475,7 @@ var derEncodedSelfSignedCert = Certificate{ ...@@ -475,7 +475,7 @@ var derEncodedSelfSignedCert = Certificate{
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}},
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}},
}, },
Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}}, Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}},
Subject: RDNSequence{ Subject: RDNSequence{
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}},
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}}, RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}},
......
...@@ -464,11 +464,15 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) ...@@ -464,11 +464,15 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
if v.Type() == rawValueType { if v.Type() == rawValueType {
rv := v.Interface().(RawValue) rv := v.Interface().(RawValue)
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound}) if len(rv.FullBytes) != 0 {
if err != nil { _, err = out.Write(rv.FullBytes)
return } else {
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
if err != nil {
return
}
_, err = out.Write(rv.Bytes)
} }
_, err = out.Write(rv.Bytes)
return return
} }
......
...@@ -163,7 +163,7 @@ func (z *Int) Binomial(n, k int64) *Int { ...@@ -163,7 +163,7 @@ func (z *Int) Binomial(n, k int64) *Int {
// Quo sets z to the quotient x/y for y != 0 and returns z. // Quo sets z to the quotient x/y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs. // If y == 0, a division-by-zero run-time panic occurs.
// See QuoRem for more details. // Quo implements truncated division (like Go); see QuoRem for more details.
func (z *Int) Quo(x, y *Int) *Int { func (z *Int) Quo(x, y *Int) *Int {
z.abs, _ = z.abs.div(nil, x.abs, y.abs) z.abs, _ = z.abs.div(nil, x.abs, y.abs)
z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign
...@@ -172,7 +172,7 @@ func (z *Int) Quo(x, y *Int) *Int { ...@@ -172,7 +172,7 @@ func (z *Int) Quo(x, y *Int) *Int {
// Rem sets z to the remainder x%y for y != 0 and returns z. // Rem sets z to the remainder x%y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs. // If y == 0, a division-by-zero run-time panic occurs.
// See QuoRem for more details. // Rem implements truncated modulus (like Go); see QuoRem for more details.
func (z *Int) Rem(x, y *Int) *Int { func (z *Int) Rem(x, y *Int) *Int {
_, z.abs = nat(nil).div(z.abs, x.abs, y.abs) _, z.abs = nat(nil).div(z.abs, x.abs, y.abs)
z.neg = len(z.abs) > 0 && x.neg // 0 has no sign z.neg = len(z.abs) > 0 && x.neg // 0 has no sign
...@@ -198,7 +198,7 @@ func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) { ...@@ -198,7 +198,7 @@ func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) {
// Div sets z to the quotient x/y for y != 0 and returns z. // Div sets z to the quotient x/y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs. // If y == 0, a division-by-zero run-time panic occurs.
// See DivMod for more details. // Div implements Euclidean division (unlike Go); see DivMod for more details.
func (z *Int) Div(x, y *Int) *Int { func (z *Int) Div(x, y *Int) *Int {
y_neg := y.neg // z may be an alias for y y_neg := y.neg // z may be an alias for y
var r Int var r Int
...@@ -215,7 +215,7 @@ func (z *Int) Div(x, y *Int) *Int { ...@@ -215,7 +215,7 @@ func (z *Int) Div(x, y *Int) *Int {
// Mod sets z to the modulus x%y for y != 0 and returns z. // Mod sets z to the modulus x%y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs. // If y == 0, a division-by-zero run-time panic occurs.
// See DivMod for more details. // Mod implements Euclidean modulus (unlike Go); see DivMod for more details.
func (z *Int) Mod(x, y *Int) *Int { func (z *Int) Mod(x, y *Int) *Int {
y0 := y // save y y0 := y // save y
if z == y || alias(z.abs, y.abs) { if z == y || alias(z.abs, y.abs) {
......
...@@ -301,6 +301,9 @@ func TestGetString(t *testing.T) { ...@@ -301,6 +301,9 @@ func TestGetString(t *testing.T) {
func TestSetString(t *testing.T) { func TestSetString(t *testing.T) {
tmp := new(Int) tmp := new(Int)
for i, test := range stringTests { for i, test := range stringTests {
// initialize to a non-zero value so that issues with parsing
// 0 are detected
tmp.SetInt64(1234567890)
n1, ok1 := new(Int).SetString(test.in, test.base) n1, ok1 := new(Int).SetString(test.in, test.base)
n2, ok2 := tmp.SetString(test.in, test.base) n2, ok2 := tmp.SetString(test.in, test.base)
expected := NewInt(test.val) expected := NewInt(test.val)
......
...@@ -646,7 +646,7 @@ func (z nat) scan(r io.RuneScanner, base int) (nat, int, os.Error) { ...@@ -646,7 +646,7 @@ func (z nat) scan(r io.RuneScanner, base int) (nat, int, os.Error) {
} }
} }
case os.EOF: case os.EOF:
return z, 10, nil return z.make(0), 10, nil
default: default:
return z, 10, err return z, 10, err
} }
......
...@@ -27,9 +27,13 @@ func NewRat(a, b int64) *Rat { ...@@ -27,9 +27,13 @@ func NewRat(a, b int64) *Rat {
// SetFrac sets z to a/b and returns z. // SetFrac sets z to a/b and returns z.
func (z *Rat) SetFrac(a, b *Int) *Rat { func (z *Rat) SetFrac(a, b *Int) *Rat {
z.a.Set(a)
z.a.neg = a.neg != b.neg z.a.neg = a.neg != b.neg
z.b = z.b.set(b.abs) babs := b.abs
if &z.a == b || alias(z.a.abs, babs) {
babs = nat(nil).set(babs) // make a copy
}
z.a.abs = z.a.abs.set(a.abs)
z.b = z.b.set(babs)
return z.norm() return z.norm()
} }
......
...@@ -330,3 +330,43 @@ func TestRatGobEncoding(t *testing.T) { ...@@ -330,3 +330,43 @@ func TestRatGobEncoding(t *testing.T) {
} }
} }
} }
func TestIssue2379(t *testing.T) {
// 1) no aliasing
q := NewRat(3, 2)
x := new(Rat)
x.SetFrac(NewInt(3), NewInt(2))
if x.Cmp(q) != 0 {
t.Errorf("1) got %s want %s", x, q)
}
// 2) aliasing of numerator
x = NewRat(2, 3)
x.SetFrac(NewInt(3), x.Num())
if x.Cmp(q) != 0 {
t.Errorf("2) got %s want %s", x, q)
}
// 3) aliasing of denominator
x = NewRat(2, 3)
x.SetFrac(x.Denom(), NewInt(2))
if x.Cmp(q) != 0 {
t.Errorf("3) got %s want %s", x, q)
}
// 4) aliasing of numerator and denominator
x = NewRat(2, 3)
x.SetFrac(x.Denom(), x.Num())
if x.Cmp(q) != 0 {
t.Errorf("4) got %s want %s", x, q)
}
// 5) numerator and denominator are the same
q = NewRat(1, 1)
x = new(Rat)
n := NewInt(7)
x.SetFrac(n, n)
if x.Cmp(q) != 0 {
t.Errorf("5) got %s want %s", x, q)
}
}
...@@ -54,11 +54,11 @@ type Reader struct { ...@@ -54,11 +54,11 @@ type Reader struct {
} }
// NewReaderSize creates a new Reader whose buffer has the specified size, // NewReaderSize creates a new Reader whose buffer has the specified size,
// which must be greater than zero. If the argument io.Reader is already a // which must be greater than one. If the argument io.Reader is already a
// Reader with large enough size, it returns the underlying Reader. // Reader with large enough size, it returns the underlying Reader.
// It returns the Reader and any error. // It returns the Reader and any error.
func NewReaderSize(rd io.Reader, size int) (*Reader, os.Error) { func NewReaderSize(rd io.Reader, size int) (*Reader, os.Error) {
if size <= 0 { if size <= 1 {
return nil, BufSizeError(size) return nil, BufSizeError(size)
} }
// Is it already a Reader? // Is it already a Reader?
...@@ -298,6 +298,17 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) { ...@@ -298,6 +298,17 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) {
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) { func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
line, err = b.ReadSlice('\n') line, err = b.ReadSlice('\n')
if err == ErrBufferFull { if err == ErrBufferFull {
// Handle the case where "\r\n" straddles the buffer.
if len(line) > 0 && line[len(line)-1] == '\r' {
// Put the '\r' back on buf and drop it from line.
// Let the next call to ReadLine check for "\r\n".
if b.r == 0 {
// should be unreachable
panic("bufio: tried to rewind past start of buffer")
}
b.r--
line = line[:len(line)-1]
}
return line, true, nil return line, true, nil
} }
...@@ -307,10 +318,11 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) { ...@@ -307,10 +318,11 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
err = nil err = nil
if line[len(line)-1] == '\n' { if line[len(line)-1] == '\n' {
line = line[:len(line)-1] drop := 1
} if len(line) > 1 && line[len(line)-2] == '\r' {
if len(line) > 0 && line[len(line)-1] == '\r' { drop = 2
line = line[:len(line)-1] }
line = line[:len(line)-drop]
} }
return return
} }
......
...@@ -137,7 +137,7 @@ var bufreaders = []bufReader{ ...@@ -137,7 +137,7 @@ var bufreaders = []bufReader{
} }
var bufsizes = []int{ var bufsizes = []int{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 2, 3, 4, 5, 6, 7, 8, 9, 10,
23, 32, 46, 64, 93, 128, 1024, 4096, 23, 32, 46, 64, 93, 128, 1024, 4096,
} }
...@@ -697,3 +697,71 @@ func TestLinesAfterRead(t *testing.T) { ...@@ -697,3 +697,71 @@ func TestLinesAfterRead(t *testing.T) {
t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err) t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
} }
} }
type readLineResult struct {
line []byte
isPrefix bool
err os.Error
}
var readLineNewlinesTests = []struct {
input string
bufSize int
expect []readLineResult
}{
{"h\r\nb\r\n", 2, []readLineResult{
{[]byte("h"), true, nil},
{nil, false, nil},
{[]byte("b"), true, nil},
{nil, false, nil},
{nil, false, os.EOF},
}},
{"hello\r\nworld\r\n", 6, []readLineResult{
{[]byte("hello"), true, nil},
{nil, false, nil},
{[]byte("world"), true, nil},
{nil, false, nil},
{nil, false, os.EOF},
}},
{"hello\rworld\r", 6, []readLineResult{
{[]byte("hello"), true, nil},
{[]byte("\rworld"), true, nil},
{[]byte("\r"), false, nil},
{nil, false, os.EOF},
}},
{"h\ri\r\n\r", 2, []readLineResult{
{[]byte("h"), true, nil},
{[]byte("\ri"), true, nil},
{nil, false, nil},
{[]byte("\r"), false, nil},
{nil, false, os.EOF},
}},
}
func TestReadLineNewlines(t *testing.T) {
for _, e := range readLineNewlinesTests {
testReadLineNewlines(t, e.input, e.bufSize, e.expect)
}
}
func testReadLineNewlines(t *testing.T, input string, bufSize int, expect []readLineResult) {
b, err := NewReaderSize(strings.NewReader(input), bufSize)
if err != nil {
t.Fatal(err)
}
for i, e := range expect {
line, isPrefix, err := b.ReadLine()
if bytes.Compare(line, e.line) != 0 {
t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line)
return
}
if isPrefix != e.isPrefix {
t.Errorf("%q call %d, isPrefix == %v, want %v", input, i, isPrefix, e.isPrefix)
return
}
if err != e.err {
t.Errorf("%q call %d, err == %v, want %v", input, i, err, e.err)
return
}
}
}
...@@ -336,13 +336,18 @@ func (b *Buffer) ReadString(delim byte) (line string, err os.Error) { ...@@ -336,13 +336,18 @@ func (b *Buffer) ReadString(delim byte) (line string, err os.Error) {
// NewBuffer creates and initializes a new Buffer using buf as its initial // NewBuffer creates and initializes a new Buffer using buf as its initial
// contents. It is intended to prepare a Buffer to read existing data. It // contents. It is intended to prepare a Buffer to read existing data. It
// can also be used to size the internal buffer for writing. To do that, // can also be used to size the internal buffer for writing. To do that,
// buf should have the desired capacity but a length of zero. // buf should have the desired capacity but a length of zero.
//
// In most cases, new(Buffer) (or just declaring a Buffer variable) is
// preferable to NewBuffer. In particular, passing a non-empty buf to
// NewBuffer and then writing to the Buffer will overwrite buf, not append to
// it.
func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} }
// NewBufferString creates and initializes a new Buffer using string s as its // NewBufferString creates and initializes a new Buffer using string s as its
// initial contents. It is intended to prepare a buffer to read an existing // initial contents. It is intended to prepare a buffer to read an existing
// string. // string. See the warnings about NewBuffer; similar issues apply here.
func NewBufferString(s string) *Buffer { func NewBufferString(s string) *Buffer {
return &Buffer{buf: []byte(s)} return &Buffer{buf: []byte(s)}
} }
...@@ -572,13 +572,18 @@ func Runes(s []byte) []int { ...@@ -572,13 +572,18 @@ func Runes(s []byte) []int {
// non-overlapping instances of old replaced by new. // non-overlapping instances of old replaced by new.
// If n < 0, there is no limit on the number of replacements. // If n < 0, there is no limit on the number of replacements.
func Replace(s, old, new []byte, n int) []byte { func Replace(s, old, new []byte, n int) []byte {
if n == 0 { m := 0
return s // avoid allocation if n != 0 {
} // Compute number of replacements.
// Compute number of replacements. m = Count(s, old)
if m := Count(s, old); m == 0 { }
return s // avoid allocation if m == 0 {
} else if n <= 0 || m < n { // Nothing to do. Just copy.
t := make([]byte, len(s))
copy(t, s)
return t
}
if n < 0 || m < n {
n = m n = m
} }
...@@ -603,3 +608,58 @@ func Replace(s, old, new []byte, n int) []byte { ...@@ -603,3 +608,58 @@ func Replace(s, old, new []byte, n int) []byte {
w += copy(t[w:], s[start:]) w += copy(t[w:], s[start:])
return t[0:w] return t[0:w]
} }
// EqualFold reports whether s and t, interpreted as UTF-8 strings,
// are equal under Unicode case-folding.
func EqualFold(s, t []byte) bool {
for len(s) != 0 && len(t) != 0 {
// Extract first rune from each.
var sr, tr int
if s[0] < utf8.RuneSelf {
sr, s = int(s[0]), s[1:]
} else {
r, size := utf8.DecodeRune(s)
sr, s = r, s[size:]
}
if t[0] < utf8.RuneSelf {
tr, t = int(t[0]), t[1:]
} else {
r, size := utf8.DecodeRune(t)
tr, t = r, t[size:]
}
// If they match, keep going; if not, return false.
// Easy case.
if tr == sr {
continue
}
// Make sr < tr to simplify what follows.
if tr < sr {
tr, sr = sr, tr
}
// Fast check for ASCII.
if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' {
// ASCII, and sr is upper case. tr must be lower case.
if tr == sr+'a'-'A' {
continue
}
return false
}
// General case. SimpleFold(x) returns the next equivalent rune > x
// or wraps around to smaller values.
r := unicode.SimpleFold(sr)
for r != sr && r < tr {
r = unicode.SimpleFold(r)
}
if r == tr {
continue
}
return false
}
// One string is empty. Are both?
return len(s) == len(t)
}
...@@ -829,9 +829,15 @@ var ReplaceTests = []ReplaceTest{ ...@@ -829,9 +829,15 @@ var ReplaceTests = []ReplaceTest{
func TestReplace(t *testing.T) { func TestReplace(t *testing.T) {
for _, tt := range ReplaceTests { for _, tt := range ReplaceTests {
if s := string(Replace([]byte(tt.in), []byte(tt.old), []byte(tt.new), tt.n)); s != tt.out { in := append([]byte(tt.in), "<spare>"...)
in = in[:len(tt.in)]
out := Replace(in, []byte(tt.old), []byte(tt.new), tt.n)
if s := string(out); s != tt.out {
t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out) t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out)
} }
if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] {
t.Errorf("Replace(%q, %q, %q, %d) didn't copy", tt.in, tt.old, tt.new, tt.n)
}
} }
} }
...@@ -856,3 +862,31 @@ func TestTitle(t *testing.T) { ...@@ -856,3 +862,31 @@ func TestTitle(t *testing.T) {
} }
} }
} }
var EqualFoldTests = []struct {
s, t string
out bool
}{
{"abc", "abc", true},
{"ABcd", "ABcd", true},
{"123abc", "123ABC", true},
{"αβδ", "ΑΒΔ", true},
{"abc", "xyz", false},
{"abc", "XYZ", false},
{"abcdefghijk", "abcdefghijX", false},
{"abcdefghijk", "abcdefghij\u212A", true},
{"abcdefghijK", "abcdefghij\u212A", true},
{"abcdefghijkz", "abcdefghij\u212Ay", false},
{"abcdefghijKz", "abcdefghij\u212Ay", false},
}
func TestEqualFold(t *testing.T) {
for _, tt := range EqualFoldTests {
if out := EqualFold([]byte(tt.s), []byte(tt.t)); out != tt.out {
t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.s, tt.t, out, tt.out)
}
if out := EqualFold([]byte(tt.t), []byte(tt.s)); out != tt.out {
t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.t, tt.s, out, tt.out)
}
}
}
...@@ -50,7 +50,7 @@ import "math" ...@@ -50,7 +50,7 @@ import "math"
// Asin returns the inverse sine of x. // Asin returns the inverse sine of x.
func Asin(x complex128) complex128 { func Asin(x complex128) complex128 {
if imag(x) == 0 { if imag(x) == 0 {
if math.Fabs(real(x)) > 1 { if math.Abs(real(x)) > 1 {
return complex(math.Pi/2, 0) // DOMAIN error return complex(math.Pi/2, 0) // DOMAIN error
} }
return complex(math.Asin(real(x)), 0) return complex(math.Asin(real(x)), 0)
...@@ -67,7 +67,7 @@ func Asin(x complex128) complex128 { ...@@ -67,7 +67,7 @@ func Asin(x complex128) complex128 {
func Asinh(x complex128) complex128 { func Asinh(x complex128) complex128 {
// TODO check range // TODO check range
if imag(x) == 0 { if imag(x) == 0 {
if math.Fabs(real(x)) > 1 { if math.Abs(real(x)) > 1 {
return complex(math.Pi/2, 0) // DOMAIN error return complex(math.Pi/2, 0) // DOMAIN error
} }
return complex(math.Asinh(real(x)), 0) return complex(math.Asinh(real(x)), 0)
......
...@@ -122,7 +122,7 @@ func Cosh(x complex128) complex128 { ...@@ -122,7 +122,7 @@ func Cosh(x complex128) complex128 {
// calculate sinh and cosh // calculate sinh and cosh
func sinhcosh(x float64) (sh, ch float64) { func sinhcosh(x float64) (sh, ch float64) {
if math.Fabs(x) <= 0.5 { if math.Abs(x) <= 0.5 {
return math.Sinh(x), math.Cosh(x) return math.Sinh(x), math.Cosh(x)
} }
e := math.Exp(x) e := math.Exp(x)
......
...@@ -76,7 +76,7 @@ func Sqrt(x complex128) complex128 { ...@@ -76,7 +76,7 @@ func Sqrt(x complex128) complex128 {
b := imag(x) b := imag(x)
var scale float64 var scale float64
// Rescale to avoid internal overflow or underflow. // Rescale to avoid internal overflow or underflow.
if math.Fabs(a) > 4 || math.Fabs(b) > 4 { if math.Abs(a) > 4 || math.Abs(b) > 4 {
a *= 0.25 a *= 0.25
b *= 0.25 b *= 0.25
scale = 2 scale = 2
...@@ -89,11 +89,11 @@ func Sqrt(x complex128) complex128 { ...@@ -89,11 +89,11 @@ func Sqrt(x complex128) complex128 {
var t float64 var t float64
if a > 0 { if a > 0 {
t = math.Sqrt(0.5*r + 0.5*a) t = math.Sqrt(0.5*r + 0.5*a)
r = scale * math.Fabs((0.5*b)/t) r = scale * math.Abs((0.5*b)/t)
t *= scale t *= scale
} else { } else {
r = math.Sqrt(0.5*r - 0.5*a) r = math.Sqrt(0.5*r - 0.5*a)
t = scale * math.Fabs((0.5*b)/r) t = scale * math.Abs((0.5*b)/r)
r *= scale r *= scale
} }
if b < 0 { if b < 0 {
......
...@@ -58,7 +58,7 @@ import "math" ...@@ -58,7 +58,7 @@ import "math"
// Tan returns the tangent of x. // Tan returns the tangent of x.
func Tan(x complex128) complex128 { func Tan(x complex128) complex128 {
d := math.Cos(2*real(x)) + math.Cosh(2*imag(x)) d := math.Cos(2*real(x)) + math.Cosh(2*imag(x))
if math.Fabs(d) < 0.25 { if math.Abs(d) < 0.25 {
d = tanSeries(x) d = tanSeries(x)
} }
if d == 0 { if d == 0 {
...@@ -109,8 +109,8 @@ func reducePi(x float64) float64 { ...@@ -109,8 +109,8 @@ func reducePi(x float64) float64 {
// Taylor series expansion for cosh(2y) - cos(2x) // Taylor series expansion for cosh(2y) - cos(2x)
func tanSeries(z complex128) float64 { func tanSeries(z complex128) float64 {
const MACHEP = 1.0 / (1 << 53) const MACHEP = 1.0 / (1 << 53)
x := math.Fabs(2 * real(z)) x := math.Abs(2 * real(z))
y := math.Fabs(2 * imag(z)) y := math.Abs(2 * imag(z))
x = reducePi(x) x = reducePi(x)
x = x * x x = x * x
y = y * y y = y * y
...@@ -139,7 +139,7 @@ func tanSeries(z complex128) float64 { ...@@ -139,7 +139,7 @@ func tanSeries(z complex128) float64 {
t = y2 - x2 t = y2 - x2
t /= f t /= f
d += t d += t
if math.Fabs(t/d) <= MACHEP { if math.Abs(t/d) <= MACHEP {
break break
} }
} }
...@@ -174,7 +174,7 @@ func tanSeries(z complex128) float64 { ...@@ -174,7 +174,7 @@ func tanSeries(z complex128) float64 {
// Cot returns the cotangent of x. // Cot returns the cotangent of x.
func Cot(x complex128) complex128 { func Cot(x complex128) complex128 {
d := math.Cosh(2*imag(x)) - math.Cos(2*real(x)) d := math.Cosh(2*imag(x)) - math.Cos(2*real(x))
if math.Fabs(d) < 0.25 { if math.Abs(d) < 0.25 {
d = tanSeries(x) d = tanSeries(x)
} }
if d == 0 { if d == 0 {
......
...@@ -6,32 +6,46 @@ package heap_test ...@@ -6,32 +6,46 @@ package heap_test
import ( import (
"testing" "testing"
"container/vector"
. "container/heap" . "container/heap"
) )
type myHeap struct { type myHeap []int
// A vector.Vector implements sort.Interface except for Less,
// and it implements Push and Pop as required for heap.Interface. func (h *myHeap) Less(i, j int) bool {
vector.Vector return (*h)[i] < (*h)[j]
}
func (h *myHeap) Swap(i, j int) {
(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
}
func (h *myHeap) Len() int {
return len(*h)
}
func (h *myHeap) Pop() (v interface{}) {
*h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1]
return
} }
func (h *myHeap) Less(i, j int) bool { return h.At(i).(int) < h.At(j).(int) } func (h *myHeap) Push(v interface{}) {
*h = append(*h, v.(int))
}
func (h *myHeap) verify(t *testing.T, i int) { func (h myHeap) verify(t *testing.T, i int) {
n := h.Len() n := h.Len()
j1 := 2*i + 1 j1 := 2*i + 1
j2 := 2*i + 2 j2 := 2*i + 2
if j1 < n { if j1 < n {
if h.Less(j1, i) { if h.Less(j1, i) {
t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j1)) t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j1])
return return
} }
h.verify(t, j1) h.verify(t, j1)
} }
if j2 < n { if j2 < n {
if h.Less(j2, i) { if h.Less(j2, i) {
t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j2)) t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j2])
return return
} }
h.verify(t, j2) h.verify(t, j2)
......
// 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 vector implements containers for managing sequences of elements.
// Vectors grow and shrink dynamically as necessary.
package vector
// Vector is a container for numbered sequences of elements of type interface{}.
// A vector's length and capacity adjusts automatically as necessary.
// The zero value for Vector is an empty vector ready to use.
type Vector []interface{}
// IntVector is a container for numbered sequences of elements of type int.
// A vector's length and capacity adjusts automatically as necessary.
// The zero value for IntVector is an empty vector ready to use.
type IntVector []int
// StringVector is a container for numbered sequences of elements of type string.
// A vector's length and capacity adjusts automatically as necessary.
// The zero value for StringVector is an empty vector ready to use.
type StringVector []string
// Initial underlying array size
const initialSize = 8
// Partial sort.Interface support
// LessInterface provides partial support of the sort.Interface.
type LessInterface interface {
Less(y interface{}) bool
}
// Less returns a boolean denoting whether the i'th element is less than the j'th element.
func (p *Vector) Less(i, j int) bool { return (*p)[i].(LessInterface).Less((*p)[j]) }
// sort.Interface support
// Less returns a boolean denoting whether the i'th element is less than the j'th element.
func (p *IntVector) Less(i, j int) bool { return (*p)[i] < (*p)[j] }
// Less returns a boolean denoting whether the i'th element is less than the j'th element.
func (p *StringVector) Less(i, j int) bool { return (*p)[i] < (*p)[j] }
// 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.
// CAUTION: If this file is not vector.go, it was generated
// automatically from vector.go - DO NOT EDIT in that case!
package vector
func (p *IntVector) realloc(length, capacity int) (b []int) {
if capacity < initialSize {
capacity = initialSize
}
if capacity < length {
capacity = length
}
b = make(IntVector, length, capacity)
copy(b, *p)
*p = b
return
}
// Insert n elements at position i.
func (p *IntVector) Expand(i, n int) {
a := *p
// make sure we have enough space
len0 := len(a)
len1 := len0 + n
if len1 <= cap(a) {
// enough space - just expand
a = a[0:len1]
} else {
// not enough space - double capacity
capb := cap(a) * 2
if capb < len1 {
// still not enough - use required length
capb = len1
}
// capb >= len1
a = p.realloc(len1, capb)
}
// make a hole
for j := len0 - 1; j >= i; j-- {
a[j+n] = a[j]
}
*p = a
}
// Insert n elements at the end of a vector.
func (p *IntVector) Extend(n int) { p.Expand(len(*p), n) }
// Resize changes the length and capacity of a vector.
// If the new length is shorter than the current length, Resize discards
// trailing elements. If the new length is longer than the current length,
// Resize adds the respective zero values for the additional elements. The capacity
// parameter is ignored unless the new length or capacity is longer than the current
// capacity. The resized vector's capacity may be larger than the requested capacity.
func (p *IntVector) Resize(length, capacity int) *IntVector {
a := *p
if length > cap(a) || capacity > cap(a) {
// not enough space or larger capacity requested explicitly
a = p.realloc(length, capacity)
} else if length < len(a) {
// clear trailing elements
for i := range a[length:] {
var zero int
a[length+i] = zero
}
}
*p = a[0:length]
return p
}
// Len returns the number of elements in the vector.
// Same as len(*p).
func (p *IntVector) Len() int { return len(*p) }
// Cap returns the capacity of the vector; that is, the
// maximum length the vector can grow without resizing.
// Same as cap(*p).
func (p *IntVector) Cap() int { return cap(*p) }
// At returns the i'th element of the vector.
func (p *IntVector) At(i int) int { return (*p)[i] }
// Set sets the i'th element of the vector to value x.
func (p *IntVector) Set(i int, x int) { (*p)[i] = x }
// Last returns the element in the vector of highest index.
func (p *IntVector) Last() int { return (*p)[len(*p)-1] }
// Copy makes a copy of the vector and returns it.
func (p *IntVector) Copy() IntVector {
arr := make(IntVector, len(*p))
copy(arr, *p)
return arr
}
// Insert inserts into the vector an element of value x before
// the current element at index i.
func (p *IntVector) Insert(i int, x int) {
p.Expand(i, 1)
(*p)[i] = x
}
// Delete deletes the i'th element of the vector. The gap is closed so the old
// element at index i+1 has index i afterwards.
func (p *IntVector) Delete(i int) {
a := *p
n := len(a)
copy(a[i:n-1], a[i+1:n])
var zero int
a[n-1] = zero // support GC, zero out entry
*p = a[0 : n-1]
}
// InsertVector inserts into the vector the contents of the vector
// x such that the 0th element of x appears at index i after insertion.
func (p *IntVector) InsertVector(i int, x *IntVector) {
b := *x
p.Expand(i, len(b))
copy((*p)[i:i+len(b)], b)
}
// Cut deletes elements i through j-1, inclusive.
func (p *IntVector) Cut(i, j int) {
a := *p
n := len(a)
m := n - (j - i)
copy(a[i:m], a[j:n])
for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector.
var zero int
a[k] = zero // support GC, zero out entries
}
*p = a[0:m]
}
// Slice returns a new sub-vector by slicing the old one to extract slice [i:j].
// The elements are copied. The original vector is unchanged.
func (p *IntVector) Slice(i, j int) *IntVector {
var s IntVector
s.realloc(j-i, 0) // will fail in Init() if j < i
copy(s, (*p)[i:j])
return &s
}
// Convenience wrappers
// Push appends x to the end of the vector.
func (p *IntVector) Push(x int) { p.Insert(len(*p), x) }
// Pop deletes the last element of the vector.
func (p *IntVector) Pop() int {
a := *p
i := len(a) - 1
x := a[i]
var zero int
a[i] = zero // support GC, zero out entry
*p = a[0:i]
return x
}
// AppendVector appends the entire vector x to the end of this vector.
func (p *IntVector) AppendVector(x *IntVector) { p.InsertVector(len(*p), x) }
// Swap exchanges the elements at indexes i and j.
func (p *IntVector) Swap(i, j int) {
a := *p
a[i], a[j] = a[j], a[i]
}
// Do calls function f for each element of the vector, in order.
// The behavior of Do is undefined if f changes *p.
func (p *IntVector) Do(f func(elem int)) {
for _, e := range *p {
f(e)
}
}
// 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.
// CAUTION: If this file is not vector_test.go, it was generated
// automatically from vector_test.go - DO NOT EDIT in that case!
package vector
import "testing"
func TestIntZeroLen(t *testing.T) {
a := new(IntVector)
if a.Len() != 0 {
t.Errorf("%T: B1) expected 0, got %d", a, a.Len())
}
if len(*a) != 0 {
t.Errorf("%T: B2) expected 0, got %d", a, len(*a))
}
var b IntVector
if b.Len() != 0 {
t.Errorf("%T: B3) expected 0, got %d", b, b.Len())
}
if len(b) != 0 {
t.Errorf("%T: B4) expected 0, got %d", b, len(b))
}
}
func TestIntResize(t *testing.T) {
var a IntVector
checkSize(t, &a, 0, 0)
checkSize(t, a.Resize(0, 5), 0, 5)
checkSize(t, a.Resize(1, 0), 1, 5)
checkSize(t, a.Resize(10, 0), 10, 10)
checkSize(t, a.Resize(5, 0), 5, 10)
checkSize(t, a.Resize(3, 8), 3, 10)
checkSize(t, a.Resize(0, 100), 0, 100)
checkSize(t, a.Resize(11, 100), 11, 100)
}
func TestIntResize2(t *testing.T) {
var a IntVector
checkSize(t, &a, 0, 0)
a.Push(int2IntValue(1))
a.Push(int2IntValue(2))
a.Push(int2IntValue(3))
a.Push(int2IntValue(4))
checkSize(t, &a, 4, 4)
checkSize(t, a.Resize(10, 0), 10, 10)
for i := 4; i < a.Len(); i++ {
if a.At(i) != intzero {
t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, intzero, a.At(i))
}
}
for i := 4; i < len(a); i++ {
if a[i] != intzero {
t.Errorf("%T: expected a[%d] == %v; found %v", a, i, intzero, a[i])
}
}
}
func checkIntZero(t *testing.T, a *IntVector, i int) {
for j := 0; j < i; j++ {
if a.At(j) == intzero {
t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j))
}
if (*a)[j] == intzero {
t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j])
}
}
for ; i < a.Len(); i++ {
if a.At(i) != intzero {
t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, intzero, a.At(i))
}
if (*a)[i] != intzero {
t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, intzero, (*a)[i])
}
}
}
func TestIntTrailingElements(t *testing.T) {
var a IntVector
for i := 0; i < 10; i++ {
a.Push(int2IntValue(i + 1))
}
checkIntZero(t, &a, 10)
checkSize(t, &a, 10, 16)
checkSize(t, a.Resize(5, 0), 5, 16)
checkSize(t, a.Resize(10, 0), 10, 16)
checkIntZero(t, &a, 5)
}
func TestIntAccess(t *testing.T) {
const n = 100
var a IntVector
a.Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2IntValue(val(i)))
}
for i := 0; i < n; i++ {
if elem2IntValue(a.At(i)) != int2IntValue(val(i)) {
t.Error(i)
}
}
var b IntVector
b.Resize(n, 0)
for i := 0; i < n; i++ {
b[i] = int2IntValue(val(i))
}
for i := 0; i < n; i++ {
if elem2IntValue(b[i]) != int2IntValue(val(i)) {
t.Error(i)
}
}
}
func TestIntInsertDeleteClear(t *testing.T) {
const n = 100
var a IntVector
for i := 0; i < n; i++ {
if a.Len() != i {
t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i)
}
a.Insert(0, int2IntValue(val(i)))
if elem2IntValue(a.Last()) != int2IntValue(val(0)) {
t.Errorf("%T: B", a)
}
}
for i := n - 1; i >= 0; i-- {
if elem2IntValue(a.Last()) != int2IntValue(val(0)) {
t.Errorf("%T: C", a)
}
if elem2IntValue(a.At(0)) != int2IntValue(val(i)) {
t.Errorf("%T: D", a)
}
if elem2IntValue(a[0]) != int2IntValue(val(i)) {
t.Errorf("%T: D2", a)
}
a.Delete(0)
if a.Len() != i {
t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i)
}
}
if a.Len() != 0 {
t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a))
}
for i := 0; i < n; i++ {
a.Push(int2IntValue(val(i)))
if a.Len() != i+1 {
t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1)
}
if len(a) != i+1 {
t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1)
}
if elem2IntValue(a.Last()) != int2IntValue(val(i)) {
t.Errorf("%T: H", a)
}
}
a.Resize(0, 0)
if a.Len() != 0 {
t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a))
}
const m = 5
for j := 0; j < m; j++ {
a.Push(int2IntValue(j))
for i := 0; i < n; i++ {
x := val(i)
a.Push(int2IntValue(x))
if elem2IntValue(a.Pop()) != int2IntValue(x) {
t.Errorf("%T: J", a)
}
if a.Len() != j+1 {
t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1)
}
if len(a) != j+1 {
t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1)
}
}
}
if a.Len() != m {
t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m)
}
if len(a) != m {
t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m)
}
}
func verify_sliceInt(t *testing.T, x *IntVector, elt, i, j int) {
for k := i; k < j; k++ {
if elem2IntValue(x.At(k)) != int2IntValue(elt) {
t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt))
}
}
s := x.Slice(i, j)
for k, n := 0, j-i; k < n; k++ {
if elem2IntValue(s.At(k)) != int2IntValue(elt) {
t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt))
}
}
}
func verify_patternInt(t *testing.T, x *IntVector, a, b, c int) {
n := a + b + c
if x.Len() != n {
t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n)
}
if len(*x) != n {
t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n)
}
verify_sliceInt(t, x, 0, 0, a)
verify_sliceInt(t, x, 1, a, a+b)
verify_sliceInt(t, x, 0, a+b, n)
}
func make_vectorInt(elt, len int) *IntVector {
x := new(IntVector).Resize(len, 0)
for i := 0; i < len; i++ {
x.Set(i, int2IntValue(elt))
}
return x
}
func TestIntInsertVector(t *testing.T) {
// 1
a := make_vectorInt(0, 0)
b := make_vectorInt(1, 10)
a.InsertVector(0, b)
verify_patternInt(t, a, 0, 10, 0)
// 2
a = make_vectorInt(0, 10)
b = make_vectorInt(1, 0)
a.InsertVector(5, b)
verify_patternInt(t, a, 5, 0, 5)
// 3
a = make_vectorInt(0, 10)
b = make_vectorInt(1, 3)
a.InsertVector(3, b)
verify_patternInt(t, a, 3, 3, 7)
// 4
a = make_vectorInt(0, 10)
b = make_vectorInt(1, 1000)
a.InsertVector(8, b)
verify_patternInt(t, a, 8, 1000, 2)
}
func TestIntDo(t *testing.T) {
const n = 25
const salt = 17
a := new(IntVector).Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2IntValue(salt*i))
}
count := 0
a.Do(func(e int) {
i := intf2IntValue(e)
if i != int2IntValue(count*salt) {
t.Error(tname(a), "value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(a), "should visit", n, "values; did visit", count)
}
b := new(IntVector).Resize(n, 0)
for i := 0; i < n; i++ {
(*b)[i] = int2IntValue(salt * i)
}
count = 0
b.Do(func(e int) {
i := intf2IntValue(e)
if i != int2IntValue(count*salt) {
t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(b), "b) should visit", n, "values; did visit", count)
}
var c IntVector
c.Resize(n, 0)
for i := 0; i < n; i++ {
c[i] = int2IntValue(salt * i)
}
count = 0
c.Do(func(e int) {
i := intf2IntValue(e)
if i != int2IntValue(count*salt) {
t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(c), "c) should visit", n, "values; did visit", count)
}
}
func TestIntVectorCopy(t *testing.T) {
// verify Copy() returns a copy, not simply a slice of the original vector
const Len = 10
var src IntVector
for i := 0; i < Len; i++ {
src.Push(int2IntValue(i * i))
}
dest := src.Copy()
for i := 0; i < Len; i++ {
src[i] = int2IntValue(-1)
v := elem2IntValue(dest[i])
if v != int2IntValue(i*i) {
t.Error(tname(src), "expected", i*i, "got", v)
}
}
}
// 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 vector
import (
"fmt"
"sort"
"testing"
)
var (
zero interface{}
intzero int
strzero string
)
func int2Value(x int) int { return x }
func int2IntValue(x int) int { return x }
func int2StrValue(x int) string { return string(x) }
func elem2Value(x interface{}) int { return x.(int) }
func elem2IntValue(x int) int { return x }
func elem2StrValue(x string) string { return x }
func intf2Value(x interface{}) int { return x.(int) }
func intf2IntValue(x interface{}) int { return x.(int) }
func intf2StrValue(x interface{}) string { return x.(string) }
type VectorInterface interface {
Len() int
Cap() int
}
func checkSize(t *testing.T, v VectorInterface, len, cap int) {
if v.Len() != len {
t.Errorf("%T expected len = %d; found %d", v, len, v.Len())
}
if v.Cap() < cap {
t.Errorf("%T expected cap >= %d; found %d", v, cap, v.Cap())
}
}
func val(i int) int { return i*991 - 1234 }
func TestSorting(t *testing.T) {
const n = 100
a := new(IntVector).Resize(n, 0)
for i := n - 1; i >= 0; i-- {
a.Set(i, n-1-i)
}
if sort.IsSorted(a) {
t.Error("int vector not sorted")
}
b := new(StringVector).Resize(n, 0)
for i := n - 1; i >= 0; i-- {
b.Set(i, fmt.Sprint(n-1-i))
}
if sort.IsSorted(b) {
t.Error("string vector not sorted")
}
}
func tname(x interface{}) string { return fmt.Sprintf("%T: ", x) }
// 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 vector
import (
"fmt"
"runtime"
"strings"
"testing"
)
const memTestN = 1000000
func s(n uint64) string {
str := fmt.Sprintf("%d", n)
lens := len(str)
a := make([]string, (lens+2)/3)
start := lens
for i := range a {
start -= 3
if start < 0 {
start = 0
}
a[len(a)-i-1] = str[start:lens]
lens -= 3
}
return strings.Join(a, " ")
}
func TestVectorNums(t *testing.T) {
if testing.Short() {
return
}
var v Vector
c := int(0)
runtime.GC()
m0 := runtime.MemStats
v.Resize(memTestN, memTestN)
for i := 0; i < memTestN; i++ {
v.Set(i, c)
}
runtime.GC()
m := runtime.MemStats
v.Resize(0, 0)
runtime.GC()
n := m.Alloc - m0.Alloc
t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN)
}
func TestIntVectorNums(t *testing.T) {
if testing.Short() {
return
}
var v IntVector
c := int(0)
runtime.GC()
m0 := runtime.MemStats
v.Resize(memTestN, memTestN)
for i := 0; i < memTestN; i++ {
v.Set(i, c)
}
runtime.GC()
m := runtime.MemStats
v.Resize(0, 0)
runtime.GC()
n := m.Alloc - m0.Alloc
t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN)
}
func TestStringVectorNums(t *testing.T) {
if testing.Short() {
return
}
var v StringVector
c := ""
runtime.GC()
m0 := runtime.MemStats
v.Resize(memTestN, memTestN)
for i := 0; i < memTestN; i++ {
v.Set(i, c)
}
runtime.GC()
m := runtime.MemStats
v.Resize(0, 0)
runtime.GC()
n := m.Alloc - m0.Alloc
t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN)
}
func BenchmarkVectorNums(b *testing.B) {
c := int(0)
var v Vector
b.StopTimer()
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
v.Push(c)
}
}
func BenchmarkIntVectorNums(b *testing.B) {
c := int(0)
var v IntVector
b.StopTimer()
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
v.Push(c)
}
}
func BenchmarkStringVectorNums(b *testing.B) {
c := ""
var v StringVector
b.StopTimer()
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
v.Push(c)
}
}
// 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.
// CAUTION: If this file is not vector.go, it was generated
// automatically from vector.go - DO NOT EDIT in that case!
package vector
func (p *StringVector) realloc(length, capacity int) (b []string) {
if capacity < initialSize {
capacity = initialSize
}
if capacity < length {
capacity = length
}
b = make(StringVector, length, capacity)
copy(b, *p)
*p = b
return
}
// Insert n elements at position i.
func (p *StringVector) Expand(i, n int) {
a := *p
// make sure we have enough space
len0 := len(a)
len1 := len0 + n
if len1 <= cap(a) {
// enough space - just expand
a = a[0:len1]
} else {
// not enough space - double capacity
capb := cap(a) * 2
if capb < len1 {
// still not enough - use required length
capb = len1
}
// capb >= len1
a = p.realloc(len1, capb)
}
// make a hole
for j := len0 - 1; j >= i; j-- {
a[j+n] = a[j]
}
*p = a
}
// Insert n elements at the end of a vector.
func (p *StringVector) Extend(n int) { p.Expand(len(*p), n) }
// Resize changes the length and capacity of a vector.
// If the new length is shorter than the current length, Resize discards
// trailing elements. If the new length is longer than the current length,
// Resize adds the respective zero values for the additional elements. The capacity
// parameter is ignored unless the new length or capacity is longer than the current
// capacity. The resized vector's capacity may be larger than the requested capacity.
func (p *StringVector) Resize(length, capacity int) *StringVector {
a := *p
if length > cap(a) || capacity > cap(a) {
// not enough space or larger capacity requested explicitly
a = p.realloc(length, capacity)
} else if length < len(a) {
// clear trailing elements
for i := range a[length:] {
var zero string
a[length+i] = zero
}
}
*p = a[0:length]
return p
}
// Len returns the number of elements in the vector.
// Same as len(*p).
func (p *StringVector) Len() int { return len(*p) }
// Cap returns the capacity of the vector; that is, the
// maximum length the vector can grow without resizing.
// Same as cap(*p).
func (p *StringVector) Cap() int { return cap(*p) }
// At returns the i'th element of the vector.
func (p *StringVector) At(i int) string { return (*p)[i] }
// Set sets the i'th element of the vector to value x.
func (p *StringVector) Set(i int, x string) { (*p)[i] = x }
// Last returns the element in the vector of highest index.
func (p *StringVector) Last() string { return (*p)[len(*p)-1] }
// Copy makes a copy of the vector and returns it.
func (p *StringVector) Copy() StringVector {
arr := make(StringVector, len(*p))
copy(arr, *p)
return arr
}
// Insert inserts into the vector an element of value x before
// the current element at index i.
func (p *StringVector) Insert(i int, x string) {
p.Expand(i, 1)
(*p)[i] = x
}
// Delete deletes the i'th element of the vector. The gap is closed so the old
// element at index i+1 has index i afterwards.
func (p *StringVector) Delete(i int) {
a := *p
n := len(a)
copy(a[i:n-1], a[i+1:n])
var zero string
a[n-1] = zero // support GC, zero out entry
*p = a[0 : n-1]
}
// InsertVector inserts into the vector the contents of the vector
// x such that the 0th element of x appears at index i after insertion.
func (p *StringVector) InsertVector(i int, x *StringVector) {
b := *x
p.Expand(i, len(b))
copy((*p)[i:i+len(b)], b)
}
// Cut deletes elements i through j-1, inclusive.
func (p *StringVector) Cut(i, j int) {
a := *p
n := len(a)
m := n - (j - i)
copy(a[i:m], a[j:n])
for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector.
var zero string
a[k] = zero // support GC, zero out entries
}
*p = a[0:m]
}
// Slice returns a new sub-vector by slicing the old one to extract slice [i:j].
// The elements are copied. The original vector is unchanged.
func (p *StringVector) Slice(i, j int) *StringVector {
var s StringVector
s.realloc(j-i, 0) // will fail in Init() if j < i
copy(s, (*p)[i:j])
return &s
}
// Convenience wrappers
// Push appends x to the end of the vector.
func (p *StringVector) Push(x string) { p.Insert(len(*p), x) }
// Pop deletes the last element of the vector.
func (p *StringVector) Pop() string {
a := *p
i := len(a) - 1
x := a[i]
var zero string
a[i] = zero // support GC, zero out entry
*p = a[0:i]
return x
}
// AppendVector appends the entire vector x to the end of this vector.
func (p *StringVector) AppendVector(x *StringVector) { p.InsertVector(len(*p), x) }
// Swap exchanges the elements at indexes i and j.
func (p *StringVector) Swap(i, j int) {
a := *p
a[i], a[j] = a[j], a[i]
}
// Do calls function f for each element of the vector, in order.
// The behavior of Do is undefined if f changes *p.
func (p *StringVector) Do(f func(elem string)) {
for _, e := range *p {
f(e)
}
}
// 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.
// CAUTION: If this file is not vector_test.go, it was generated
// automatically from vector_test.go - DO NOT EDIT in that case!
package vector
import "testing"
func TestStrZeroLen(t *testing.T) {
a := new(StringVector)
if a.Len() != 0 {
t.Errorf("%T: B1) expected 0, got %d", a, a.Len())
}
if len(*a) != 0 {
t.Errorf("%T: B2) expected 0, got %d", a, len(*a))
}
var b StringVector
if b.Len() != 0 {
t.Errorf("%T: B3) expected 0, got %d", b, b.Len())
}
if len(b) != 0 {
t.Errorf("%T: B4) expected 0, got %d", b, len(b))
}
}
func TestStrResize(t *testing.T) {
var a StringVector
checkSize(t, &a, 0, 0)
checkSize(t, a.Resize(0, 5), 0, 5)
checkSize(t, a.Resize(1, 0), 1, 5)
checkSize(t, a.Resize(10, 0), 10, 10)
checkSize(t, a.Resize(5, 0), 5, 10)
checkSize(t, a.Resize(3, 8), 3, 10)
checkSize(t, a.Resize(0, 100), 0, 100)
checkSize(t, a.Resize(11, 100), 11, 100)
}
func TestStrResize2(t *testing.T) {
var a StringVector
checkSize(t, &a, 0, 0)
a.Push(int2StrValue(1))
a.Push(int2StrValue(2))
a.Push(int2StrValue(3))
a.Push(int2StrValue(4))
checkSize(t, &a, 4, 4)
checkSize(t, a.Resize(10, 0), 10, 10)
for i := 4; i < a.Len(); i++ {
if a.At(i) != strzero {
t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, strzero, a.At(i))
}
}
for i := 4; i < len(a); i++ {
if a[i] != strzero {
t.Errorf("%T: expected a[%d] == %v; found %v", a, i, strzero, a[i])
}
}
}
func checkStrZero(t *testing.T, a *StringVector, i int) {
for j := 0; j < i; j++ {
if a.At(j) == strzero {
t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j))
}
if (*a)[j] == strzero {
t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j])
}
}
for ; i < a.Len(); i++ {
if a.At(i) != strzero {
t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, strzero, a.At(i))
}
if (*a)[i] != strzero {
t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, strzero, (*a)[i])
}
}
}
func TestStrTrailingElements(t *testing.T) {
var a StringVector
for i := 0; i < 10; i++ {
a.Push(int2StrValue(i + 1))
}
checkStrZero(t, &a, 10)
checkSize(t, &a, 10, 16)
checkSize(t, a.Resize(5, 0), 5, 16)
checkSize(t, a.Resize(10, 0), 10, 16)
checkStrZero(t, &a, 5)
}
func TestStrAccess(t *testing.T) {
const n = 100
var a StringVector
a.Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2StrValue(val(i)))
}
for i := 0; i < n; i++ {
if elem2StrValue(a.At(i)) != int2StrValue(val(i)) {
t.Error(i)
}
}
var b StringVector
b.Resize(n, 0)
for i := 0; i < n; i++ {
b[i] = int2StrValue(val(i))
}
for i := 0; i < n; i++ {
if elem2StrValue(b[i]) != int2StrValue(val(i)) {
t.Error(i)
}
}
}
func TestStrInsertDeleteClear(t *testing.T) {
const n = 100
var a StringVector
for i := 0; i < n; i++ {
if a.Len() != i {
t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i)
}
a.Insert(0, int2StrValue(val(i)))
if elem2StrValue(a.Last()) != int2StrValue(val(0)) {
t.Errorf("%T: B", a)
}
}
for i := n - 1; i >= 0; i-- {
if elem2StrValue(a.Last()) != int2StrValue(val(0)) {
t.Errorf("%T: C", a)
}
if elem2StrValue(a.At(0)) != int2StrValue(val(i)) {
t.Errorf("%T: D", a)
}
if elem2StrValue(a[0]) != int2StrValue(val(i)) {
t.Errorf("%T: D2", a)
}
a.Delete(0)
if a.Len() != i {
t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i)
}
}
if a.Len() != 0 {
t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a))
}
for i := 0; i < n; i++ {
a.Push(int2StrValue(val(i)))
if a.Len() != i+1 {
t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1)
}
if len(a) != i+1 {
t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1)
}
if elem2StrValue(a.Last()) != int2StrValue(val(i)) {
t.Errorf("%T: H", a)
}
}
a.Resize(0, 0)
if a.Len() != 0 {
t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a))
}
const m = 5
for j := 0; j < m; j++ {
a.Push(int2StrValue(j))
for i := 0; i < n; i++ {
x := val(i)
a.Push(int2StrValue(x))
if elem2StrValue(a.Pop()) != int2StrValue(x) {
t.Errorf("%T: J", a)
}
if a.Len() != j+1 {
t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1)
}
if len(a) != j+1 {
t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1)
}
}
}
if a.Len() != m {
t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m)
}
if len(a) != m {
t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m)
}
}
func verify_sliceStr(t *testing.T, x *StringVector, elt, i, j int) {
for k := i; k < j; k++ {
if elem2StrValue(x.At(k)) != int2StrValue(elt) {
t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt))
}
}
s := x.Slice(i, j)
for k, n := 0, j-i; k < n; k++ {
if elem2StrValue(s.At(k)) != int2StrValue(elt) {
t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt))
}
}
}
func verify_patternStr(t *testing.T, x *StringVector, a, b, c int) {
n := a + b + c
if x.Len() != n {
t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n)
}
if len(*x) != n {
t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n)
}
verify_sliceStr(t, x, 0, 0, a)
verify_sliceStr(t, x, 1, a, a+b)
verify_sliceStr(t, x, 0, a+b, n)
}
func make_vectorStr(elt, len int) *StringVector {
x := new(StringVector).Resize(len, 0)
for i := 0; i < len; i++ {
x.Set(i, int2StrValue(elt))
}
return x
}
func TestStrInsertVector(t *testing.T) {
// 1
a := make_vectorStr(0, 0)
b := make_vectorStr(1, 10)
a.InsertVector(0, b)
verify_patternStr(t, a, 0, 10, 0)
// 2
a = make_vectorStr(0, 10)
b = make_vectorStr(1, 0)
a.InsertVector(5, b)
verify_patternStr(t, a, 5, 0, 5)
// 3
a = make_vectorStr(0, 10)
b = make_vectorStr(1, 3)
a.InsertVector(3, b)
verify_patternStr(t, a, 3, 3, 7)
// 4
a = make_vectorStr(0, 10)
b = make_vectorStr(1, 1000)
a.InsertVector(8, b)
verify_patternStr(t, a, 8, 1000, 2)
}
func TestStrDo(t *testing.T) {
const n = 25
const salt = 17
a := new(StringVector).Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2StrValue(salt*i))
}
count := 0
a.Do(func(e string) {
i := intf2StrValue(e)
if i != int2StrValue(count*salt) {
t.Error(tname(a), "value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(a), "should visit", n, "values; did visit", count)
}
b := new(StringVector).Resize(n, 0)
for i := 0; i < n; i++ {
(*b)[i] = int2StrValue(salt * i)
}
count = 0
b.Do(func(e string) {
i := intf2StrValue(e)
if i != int2StrValue(count*salt) {
t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(b), "b) should visit", n, "values; did visit", count)
}
var c StringVector
c.Resize(n, 0)
for i := 0; i < n; i++ {
c[i] = int2StrValue(salt * i)
}
count = 0
c.Do(func(e string) {
i := intf2StrValue(e)
if i != int2StrValue(count*salt) {
t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(c), "c) should visit", n, "values; did visit", count)
}
}
func TestStrVectorCopy(t *testing.T) {
// verify Copy() returns a copy, not simply a slice of the original vector
const Len = 10
var src StringVector
for i := 0; i < Len; i++ {
src.Push(int2StrValue(i * i))
}
dest := src.Copy()
for i := 0; i < Len; i++ {
src[i] = int2StrValue(-1)
v := elem2StrValue(dest[i])
if v != int2StrValue(i*i) {
t.Error(tname(src), "expected", i*i, "got", v)
}
}
}
// 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.
// CAUTION: If this file is not vector.go, it was generated
// automatically from vector.go - DO NOT EDIT in that case!
package vector
func (p *Vector) realloc(length, capacity int) (b []interface{}) {
if capacity < initialSize {
capacity = initialSize
}
if capacity < length {
capacity = length
}
b = make(Vector, length, capacity)
copy(b, *p)
*p = b
return
}
// Insert n elements at position i.
func (p *Vector) Expand(i, n int) {
a := *p
// make sure we have enough space
len0 := len(a)
len1 := len0 + n
if len1 <= cap(a) {
// enough space - just expand
a = a[0:len1]
} else {
// not enough space - double capacity
capb := cap(a) * 2
if capb < len1 {
// still not enough - use required length
capb = len1
}
// capb >= len1
a = p.realloc(len1, capb)
}
// make a hole
for j := len0 - 1; j >= i; j-- {
a[j+n] = a[j]
}
*p = a
}
// Insert n elements at the end of a vector.
func (p *Vector) Extend(n int) { p.Expand(len(*p), n) }
// Resize changes the length and capacity of a vector.
// If the new length is shorter than the current length, Resize discards
// trailing elements. If the new length is longer than the current length,
// Resize adds the respective zero values for the additional elements. The capacity
// parameter is ignored unless the new length or capacity is longer than the current
// capacity. The resized vector's capacity may be larger than the requested capacity.
func (p *Vector) Resize(length, capacity int) *Vector {
a := *p
if length > cap(a) || capacity > cap(a) {
// not enough space or larger capacity requested explicitly
a = p.realloc(length, capacity)
} else if length < len(a) {
// clear trailing elements
for i := range a[length:] {
var zero interface{}
a[length+i] = zero
}
}
*p = a[0:length]
return p
}
// Len returns the number of elements in the vector.
// Same as len(*p).
func (p *Vector) Len() int { return len(*p) }
// Cap returns the capacity of the vector; that is, the
// maximum length the vector can grow without resizing.
// Same as cap(*p).
func (p *Vector) Cap() int { return cap(*p) }
// At returns the i'th element of the vector.
func (p *Vector) At(i int) interface{} { return (*p)[i] }
// Set sets the i'th element of the vector to value x.
func (p *Vector) Set(i int, x interface{}) { (*p)[i] = x }
// Last returns the element in the vector of highest index.
func (p *Vector) Last() interface{} { return (*p)[len(*p)-1] }
// Copy makes a copy of the vector and returns it.
func (p *Vector) Copy() Vector {
arr := make(Vector, len(*p))
copy(arr, *p)
return arr
}
// Insert inserts into the vector an element of value x before
// the current element at index i.
func (p *Vector) Insert(i int, x interface{}) {
p.Expand(i, 1)
(*p)[i] = x
}
// Delete deletes the i'th element of the vector. The gap is closed so the old
// element at index i+1 has index i afterwards.
func (p *Vector) Delete(i int) {
a := *p
n := len(a)
copy(a[i:n-1], a[i+1:n])
var zero interface{}
a[n-1] = zero // support GC, zero out entry
*p = a[0 : n-1]
}
// InsertVector inserts into the vector the contents of the vector
// x such that the 0th element of x appears at index i after insertion.
func (p *Vector) InsertVector(i int, x *Vector) {
b := *x
p.Expand(i, len(b))
copy((*p)[i:i+len(b)], b)
}
// Cut deletes elements i through j-1, inclusive.
func (p *Vector) Cut(i, j int) {
a := *p
n := len(a)
m := n - (j - i)
copy(a[i:m], a[j:n])
for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector.
var zero interface{}
a[k] = zero // support GC, zero out entries
}
*p = a[0:m]
}
// Slice returns a new sub-vector by slicing the old one to extract slice [i:j].
// The elements are copied. The original vector is unchanged.
func (p *Vector) Slice(i, j int) *Vector {
var s Vector
s.realloc(j-i, 0) // will fail in Init() if j < i
copy(s, (*p)[i:j])
return &s
}
// Convenience wrappers
// Push appends x to the end of the vector.
func (p *Vector) Push(x interface{}) { p.Insert(len(*p), x) }
// Pop deletes the last element of the vector.
func (p *Vector) Pop() interface{} {
a := *p
i := len(a) - 1
x := a[i]
var zero interface{}
a[i] = zero // support GC, zero out entry
*p = a[0:i]
return x
}
// AppendVector appends the entire vector x to the end of this vector.
func (p *Vector) AppendVector(x *Vector) { p.InsertVector(len(*p), x) }
// Swap exchanges the elements at indexes i and j.
func (p *Vector) Swap(i, j int) {
a := *p
a[i], a[j] = a[j], a[i]
}
// Do calls function f for each element of the vector, in order.
// The behavior of Do is undefined if f changes *p.
func (p *Vector) Do(f func(elem interface{})) {
for _, e := range *p {
f(e)
}
}
// 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.
// CAUTION: If this file is not vector_test.go, it was generated
// automatically from vector_test.go - DO NOT EDIT in that case!
package vector
import "testing"
func TestZeroLen(t *testing.T) {
a := new(Vector)
if a.Len() != 0 {
t.Errorf("%T: B1) expected 0, got %d", a, a.Len())
}
if len(*a) != 0 {
t.Errorf("%T: B2) expected 0, got %d", a, len(*a))
}
var b Vector
if b.Len() != 0 {
t.Errorf("%T: B3) expected 0, got %d", b, b.Len())
}
if len(b) != 0 {
t.Errorf("%T: B4) expected 0, got %d", b, len(b))
}
}
func TestResize(t *testing.T) {
var a Vector
checkSize(t, &a, 0, 0)
checkSize(t, a.Resize(0, 5), 0, 5)
checkSize(t, a.Resize(1, 0), 1, 5)
checkSize(t, a.Resize(10, 0), 10, 10)
checkSize(t, a.Resize(5, 0), 5, 10)
checkSize(t, a.Resize(3, 8), 3, 10)
checkSize(t, a.Resize(0, 100), 0, 100)
checkSize(t, a.Resize(11, 100), 11, 100)
}
func TestResize2(t *testing.T) {
var a Vector
checkSize(t, &a, 0, 0)
a.Push(int2Value(1))
a.Push(int2Value(2))
a.Push(int2Value(3))
a.Push(int2Value(4))
checkSize(t, &a, 4, 4)
checkSize(t, a.Resize(10, 0), 10, 10)
for i := 4; i < a.Len(); i++ {
if a.At(i) != zero {
t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, zero, a.At(i))
}
}
for i := 4; i < len(a); i++ {
if a[i] != zero {
t.Errorf("%T: expected a[%d] == %v; found %v", a, i, zero, a[i])
}
}
}
func checkZero(t *testing.T, a *Vector, i int) {
for j := 0; j < i; j++ {
if a.At(j) == zero {
t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j))
}
if (*a)[j] == zero {
t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j])
}
}
for ; i < a.Len(); i++ {
if a.At(i) != zero {
t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, zero, a.At(i))
}
if (*a)[i] != zero {
t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, zero, (*a)[i])
}
}
}
func TestTrailingElements(t *testing.T) {
var a Vector
for i := 0; i < 10; i++ {
a.Push(int2Value(i + 1))
}
checkZero(t, &a, 10)
checkSize(t, &a, 10, 16)
checkSize(t, a.Resize(5, 0), 5, 16)
checkSize(t, a.Resize(10, 0), 10, 16)
checkZero(t, &a, 5)
}
func TestAccess(t *testing.T) {
const n = 100
var a Vector
a.Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2Value(val(i)))
}
for i := 0; i < n; i++ {
if elem2Value(a.At(i)) != int2Value(val(i)) {
t.Error(i)
}
}
var b Vector
b.Resize(n, 0)
for i := 0; i < n; i++ {
b[i] = int2Value(val(i))
}
for i := 0; i < n; i++ {
if elem2Value(b[i]) != int2Value(val(i)) {
t.Error(i)
}
}
}
func TestInsertDeleteClear(t *testing.T) {
const n = 100
var a Vector
for i := 0; i < n; i++ {
if a.Len() != i {
t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i)
}
a.Insert(0, int2Value(val(i)))
if elem2Value(a.Last()) != int2Value(val(0)) {
t.Errorf("%T: B", a)
}
}
for i := n - 1; i >= 0; i-- {
if elem2Value(a.Last()) != int2Value(val(0)) {
t.Errorf("%T: C", a)
}
if elem2Value(a.At(0)) != int2Value(val(i)) {
t.Errorf("%T: D", a)
}
if elem2Value(a[0]) != int2Value(val(i)) {
t.Errorf("%T: D2", a)
}
a.Delete(0)
if a.Len() != i {
t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i)
}
}
if a.Len() != 0 {
t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a))
}
for i := 0; i < n; i++ {
a.Push(int2Value(val(i)))
if a.Len() != i+1 {
t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1)
}
if len(a) != i+1 {
t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1)
}
if elem2Value(a.Last()) != int2Value(val(i)) {
t.Errorf("%T: H", a)
}
}
a.Resize(0, 0)
if a.Len() != 0 {
t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a))
}
const m = 5
for j := 0; j < m; j++ {
a.Push(int2Value(j))
for i := 0; i < n; i++ {
x := val(i)
a.Push(int2Value(x))
if elem2Value(a.Pop()) != int2Value(x) {
t.Errorf("%T: J", a)
}
if a.Len() != j+1 {
t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1)
}
if len(a) != j+1 {
t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1)
}
}
}
if a.Len() != m {
t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m)
}
if len(a) != m {
t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m)
}
}
func verify_slice(t *testing.T, x *Vector, elt, i, j int) {
for k := i; k < j; k++ {
if elem2Value(x.At(k)) != int2Value(elt) {
t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt))
}
}
s := x.Slice(i, j)
for k, n := 0, j-i; k < n; k++ {
if elem2Value(s.At(k)) != int2Value(elt) {
t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt))
}
}
}
func verify_pattern(t *testing.T, x *Vector, a, b, c int) {
n := a + b + c
if x.Len() != n {
t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n)
}
if len(*x) != n {
t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n)
}
verify_slice(t, x, 0, 0, a)
verify_slice(t, x, 1, a, a+b)
verify_slice(t, x, 0, a+b, n)
}
func make_vector(elt, len int) *Vector {
x := new(Vector).Resize(len, 0)
for i := 0; i < len; i++ {
x.Set(i, int2Value(elt))
}
return x
}
func TestInsertVector(t *testing.T) {
// 1
a := make_vector(0, 0)
b := make_vector(1, 10)
a.InsertVector(0, b)
verify_pattern(t, a, 0, 10, 0)
// 2
a = make_vector(0, 10)
b = make_vector(1, 0)
a.InsertVector(5, b)
verify_pattern(t, a, 5, 0, 5)
// 3
a = make_vector(0, 10)
b = make_vector(1, 3)
a.InsertVector(3, b)
verify_pattern(t, a, 3, 3, 7)
// 4
a = make_vector(0, 10)
b = make_vector(1, 1000)
a.InsertVector(8, b)
verify_pattern(t, a, 8, 1000, 2)
}
func TestDo(t *testing.T) {
const n = 25
const salt = 17
a := new(Vector).Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2Value(salt*i))
}
count := 0
a.Do(func(e interface{}) {
i := intf2Value(e)
if i != int2Value(count*salt) {
t.Error(tname(a), "value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(a), "should visit", n, "values; did visit", count)
}
b := new(Vector).Resize(n, 0)
for i := 0; i < n; i++ {
(*b)[i] = int2Value(salt * i)
}
count = 0
b.Do(func(e interface{}) {
i := intf2Value(e)
if i != int2Value(count*salt) {
t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(b), "b) should visit", n, "values; did visit", count)
}
var c Vector
c.Resize(n, 0)
for i := 0; i < n; i++ {
c[i] = int2Value(salt * i)
}
count = 0
c.Do(func(e interface{}) {
i := intf2Value(e)
if i != int2Value(count*salt) {
t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(c), "c) should visit", n, "values; did visit", count)
}
}
func TestVectorCopy(t *testing.T) {
// verify Copy() returns a copy, not simply a slice of the original vector
const Len = 10
var src Vector
for i := 0; i < Len; i++ {
src.Push(int2Value(i * i))
}
dest := src.Copy()
for i := 0; i < Len; i++ {
src[i] = int2Value(-1)
v := elem2Value(dest[i])
if v != int2Value(i*i) {
t.Error(tname(src), "expected", i*i, "got", v)
}
}
}
// Copyright 2011 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 bcrypt
import (
"encoding/base64"
"os"
)
const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
var bcEncoding = base64.NewEncoding(alphabet)
func base64Encode(src []byte) []byte {
n := bcEncoding.EncodedLen(len(src))
dst := make([]byte, n)
bcEncoding.Encode(dst, src)
for dst[n-1] == '=' {
n--
}
return dst[:n]
}
func base64Decode(src []byte) ([]byte, os.Error) {
numOfEquals := 4 - (len(src) % 4)
for i := 0; i < numOfEquals; i++ {
src = append(src, '=')
}
dst := make([]byte, bcEncoding.DecodedLen(len(src)))
n, err := bcEncoding.Decode(dst, src)
if err != nil {
return nil, err
}
return dst[:n], nil
}
// Copyright 2011 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 bcrypt implements Provos and Mazières's bcrypt adapative hashing
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
package bcrypt
// The code is a port of Provos and Mazières's C implementation.
import (
"crypto/blowfish"
"crypto/rand"
"crypto/subtle"
"fmt"
"io"
"os"
"strconv"
)
const (
MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
)
// The error returned from CompareHashAndPassword when a password and hash do
// not match.
var MismatchedHashAndPasswordError = os.NewError("crypto/bcrypt: hashedPassword is not the hash of the given password")
// The error returned from CompareHashAndPassword when a hash is too short to
// be a bcrypt hash.
var HashTooShortError = os.NewError("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
// The error returned from CompareHashAndPassword when a hash was created with
// a bcrypt algorithm newer than this implementation.
type HashVersionTooNewError byte
func (hv HashVersionTooNewError) String() string {
return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
}
// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
type InvalidHashPrefixError byte
func (ih InvalidHashPrefixError) String() string {
return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
}
type InvalidCostError int
func (ic InvalidCostError) String() string {
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
}
const (
majorVersion = '2'
minorVersion = 'a'
maxSaltSize = 16
maxCryptedHashSize = 23
encodedSaltSize = 22
encodedHashSize = 31
minHashSize = 59
)
// magicCipherData is an IV for the 64 Blowfish encryption calls in
// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
var magicCipherData = []byte{
0x4f, 0x72, 0x70, 0x68,
0x65, 0x61, 0x6e, 0x42,
0x65, 0x68, 0x6f, 0x6c,
0x64, 0x65, 0x72, 0x53,
0x63, 0x72, 0x79, 0x44,
0x6f, 0x75, 0x62, 0x74,
}
type hashed struct {
hash []byte
salt []byte
cost uint32 // allowed range is MinCost to MaxCost
major byte
minor byte
}
// GenerateFromPassword returns the bcrypt hash of the password at the given
// cost. If the cost given is less than MinCost, the cost will be set to
// MinCost, instead. Use CompareHashAndPassword, as defined in this package,
// to compare the returned hashed password with its cleartext version.
func GenerateFromPassword(password []byte, cost int) ([]byte, os.Error) {
p, err := newFromPassword(password, cost)
if err != nil {
return nil, err
}
return p.Hash(), nil
}
// CompareHashAndPassword compares a bcrypt hashed password with its possible
// plaintext equivalent. Note: Using bytes.Equal for this job is
// insecure. Returns nil on success, or an error on failure.
func CompareHashAndPassword(hashedPassword, password []byte) os.Error {
p, err := newFromHash(hashedPassword)
if err != nil {
return err
}
otherHash, err := bcrypt(password, p.cost, p.salt)
if err != nil {
return err
}
otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
return nil
}
return MismatchedHashAndPasswordError
}
func newFromPassword(password []byte, cost int) (*hashed, os.Error) {
if cost < MinCost {
cost = DefaultCost
}
p := new(hashed)
p.major = majorVersion
p.minor = minorVersion
err := checkCost(cost)
if err != nil {
return nil, err
}
p.cost = uint32(cost)
unencodedSalt := make([]byte, maxSaltSize)
_, err = io.ReadFull(rand.Reader, unencodedSalt)
if err != nil {
return nil, err
}
p.salt = base64Encode(unencodedSalt)
hash, err := bcrypt(password, p.cost, p.salt)
if err != nil {
return nil, err
}
p.hash = hash
return p, err
}
func newFromHash(hashedSecret []byte) (*hashed, os.Error) {
if len(hashedSecret) < minHashSize {
return nil, HashTooShortError
}
p := new(hashed)
n, err := p.decodeVersion(hashedSecret)
if err != nil {
return nil, err
}
hashedSecret = hashedSecret[n:]
n, err = p.decodeCost(hashedSecret)
if err != nil {
return nil, err
}
hashedSecret = hashedSecret[n:]
// The "+2" is here because we'll have to append at most 2 '=' to the salt
// when base64 decoding it in expensiveBlowfishSetup().
p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
copy(p.salt, hashedSecret[:encodedSaltSize])
hashedSecret = hashedSecret[encodedSaltSize:]
p.hash = make([]byte, len(hashedSecret))
copy(p.hash, hashedSecret)
return p, nil
}
func bcrypt(password []byte, cost uint32, salt []byte) ([]byte, os.Error) {
cipherData := make([]byte, len(magicCipherData))
copy(cipherData, magicCipherData)
c, err := expensiveBlowfishSetup(password, cost, salt)
if err != nil {
return nil, err
}
for i := 0; i < 24; i += 8 {
for j := 0; j < 64; j++ {
c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
}
}
// Bug compatibility with C bcrypt implementations. We only encode 23 of
// the 24 bytes encrypted.
hsh := base64Encode(cipherData[:maxCryptedHashSize])
return hsh, nil
}
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, os.Error) {
csalt, err := base64Decode(salt)
if err != nil {
return nil, err
}
// Bug compatibility with C bcrypt implementations. They use the trailing
// NULL in the key string during expansion.
ckey := append(key, 0)
c, err := blowfish.NewSaltedCipher(ckey, csalt)
if err != nil {
return nil, err
}
rounds := 1 << cost
for i := 0; i < rounds; i++ {
blowfish.ExpandKey(ckey, c)
blowfish.ExpandKey(csalt, c)
}
return c, nil
}
func (p *hashed) Hash() []byte {
arr := make([]byte, 60)
arr[0] = '$'
arr[1] = p.major
n := 2
if p.minor != 0 {
arr[2] = p.minor
n = 3
}
arr[n] = '$'
n += 1
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
n += 2
arr[n] = '$'
n += 1
copy(arr[n:], p.salt)
n += encodedSaltSize
copy(arr[n:], p.hash)
n += encodedHashSize
return arr[:n]
}
func (p *hashed) decodeVersion(sbytes []byte) (int, os.Error) {
if sbytes[0] != '$' {
return -1, InvalidHashPrefixError(sbytes[0])
}
if sbytes[1] > majorVersion {
return -1, HashVersionTooNewError(sbytes[1])
}
p.major = sbytes[1]
n := 3
if sbytes[2] != '$' {
p.minor = sbytes[2]
n++
}
return n, nil
}
// sbytes should begin where decodeVersion left off.
func (p *hashed) decodeCost(sbytes []byte) (int, os.Error) {
cost, err := strconv.Atoi(string(sbytes[0:2]))
if err != nil {
return -1, err
}
err = checkCost(cost)
if err != nil {
return -1, err
}
p.cost = uint32(cost)
return 3, nil
}
func (p *hashed) String() string {
return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
}
func checkCost(cost int) os.Error {
if cost < MinCost || cost > MaxCost {
return InvalidCostError(cost)
}
return nil
}
// Copyright 2011 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 bcrypt
import (
"bytes"
"os"
"testing"
)
func TestBcryptingIsEasy(t *testing.T) {
pass := []byte("mypassword")
hp, err := GenerateFromPassword(pass, 0)
if err != nil {
t.Fatalf("GenerateFromPassword error: %s", err)
}
if CompareHashAndPassword(hp, pass) != nil {
t.Errorf("%v should hash %s correctly", hp, pass)
}
notPass := "notthepass"
err = CompareHashAndPassword(hp, []byte(notPass))
if err != MismatchedHashAndPasswordError {
t.Errorf("%v and %s should be mismatched", hp, notPass)
}
}
func TestBcryptingIsCorrect(t *testing.T) {
pass := []byte("allmine")
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
hash, err := bcrypt(pass, 10, salt)
if err != nil {
t.Fatalf("bcrypt blew up: %v", err)
}
if !bytes.HasSuffix(expectedHash, hash) {
t.Errorf("%v should be the suffix of %v", hash, expectedHash)
}
h, err := newFromHash(expectedHash)
if err != nil {
t.Errorf("Unable to parse %s: %v", string(expectedHash), err)
}
// This is not the safe way to compare these hashes. We do this only for
// testing clarity. Use bcrypt.CompareHashAndPassword()
if err == nil && !bytes.Equal(expectedHash, h.Hash()) {
t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash)
}
}
func TestTooLongPasswordsWork(t *testing.T) {
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
// One byte over the usual 56 byte limit that blowfish has
tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
hash, err := bcrypt(tooLongPass, 10, salt)
if err != nil {
t.Fatalf("bcrypt blew up on long password: %v", err)
}
if !bytes.HasSuffix(tooLongExpected, hash) {
t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
}
}
type InvalidHashTest struct {
err os.Error
hash []byte
}
var invalidTests = []InvalidHashTest{
{HashTooShortError, []byte("$2a$10$fooo")},
{HashTooShortError, []byte("$2a")},
{HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
{InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
{InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
}
func TestInvalidHashErrors(t *testing.T) {
check := func(name string, expected, err os.Error) {
if err == nil {
t.Errorf("%s: Should have returned an error", name)
}
if err != nil && err != expected {
t.Errorf("%s gave err %v but should have given %v", name, err.String(), expected.String())
}
}
for _, iht := range invalidTests {
_, err := newFromHash(iht.hash)
check("newFromHash", iht.err, err)
err = CompareHashAndPassword(iht.hash, []byte("anything"))
check("CompareHashAndPassword", iht.err, err)
}
}
func TestUnpaddedBase64Encoding(t *testing.T) {
original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
encoded := base64Encode(original)
if !bytes.Equal(encodedOriginal, encoded) {
t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
}
decoded, err := base64Decode(encodedOriginal)
if err != nil {
t.Fatalf("base64Decode blew up: %s", err)
}
if !bytes.Equal(decoded, original) {
t.Errorf("Decoded %v should have equaled %v", decoded, original)
}
}
func TestCost(t *testing.T) {
if testing.Short() {
return
}
pass := []byte("mypassword")
for c := 0; c < MinCost; c++ {
p, _ := newFromPassword(pass, c)
if p.cost != uint32(DefaultCost) {
t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
}
}
p, _ := newFromPassword(pass, 14)
if p.cost != 14 {
t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
}
hp, _ := newFromHash(p.Hash())
if p.cost != hp.cost {
t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
}
_, err := newFromPassword(pass, 32)
if err == nil {
t.Fatalf("newFromPassword: should return a cost error")
}
if err != InvalidCostError(32) {
t.Errorf("newFromPassword: should return cost error, got %#v", err)
}
}
func TestCostReturnsWithLeadingZeroes(t *testing.T) {
hp, _ := newFromPassword([]byte("abcdefgh"), 7)
cost := hp.Hash()[4:7]
expected := []byte("07$")
if !bytes.Equal(expected, cost) {
t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
}
}
func TestMinorNotRequired(t *testing.T) {
noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
h, err := newFromHash(noMinorHash)
if err != nil {
t.Fatalf("No minor hash blew up: %s", err)
}
if h.minor != 0 {
t.Errorf("Should leave minor version at 0, but was %d", h.minor)
}
if !bytes.Equal(noMinorHash, h.Hash()) {
t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
}
}
func BenchmarkEqual(b *testing.B) {
b.StopTimer()
passwd := []byte("somepasswordyoulike")
hash, _ := GenerateFromPassword(passwd, 10)
b.StartTimer()
for i := 0; i < b.N; i++ {
CompareHashAndPassword(hash, passwd)
}
}
func BenchmarkGeneration(b *testing.B) {
b.StopTimer()
passwd := []byte("mylongpassword1234")
b.StartTimer()
for i := 0; i < b.N; i++ {
GenerateFromPassword(passwd, 10)
}
}
...@@ -4,13 +4,12 @@ ...@@ -4,13 +4,12 @@
package blowfish package blowfish
func expandKey(key []byte, c *Cipher) { // ExpandKey performs a key expansion on the given *Cipher. Specifically, it
copy(c.p[0:], p[0:]) // performs the Blowfish algorithm's key schedule which sets up the *Cipher's
copy(c.s0[0:], s0[0:]) // pi and substitution tables for calls to Encrypt. This is used, primarily,
copy(c.s1[0:], s1[0:]) // by the bcrypt package to reuse the Blowfish key schedule during its
copy(c.s2[0:], s2[0:]) // set up. It's unlikely that you need to use this directly.
copy(c.s3[0:], s3[0:]) func ExpandKey(key []byte, c *Cipher) {
j := 0 j := 0
for i := 0; i < 18; i++ { for i := 0; i < 18; i++ {
var d uint32 var d uint32
...@@ -48,6 +47,98 @@ func expandKey(key []byte, c *Cipher) { ...@@ -48,6 +47,98 @@ func expandKey(key []byte, c *Cipher) {
} }
} }
// This is similar to ExpandKey, but folds the salt during the key
// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
// and specializing it here is useful.
func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
j := 0
expandedKey := make([]uint32, 18)
for i := 0; i < 18; i++ {
var d uint32
for k := 0; k < 4; k++ {
d = d<<8 | uint32(key[j])&0x000000FF
j++
if j >= len(key) {
j = 0
}
}
expandedKey[i] = d
c.p[i] ^= d
}
j = 0
expandedSalt := make([]uint32, 18)
for i := 0; i < 18; i++ {
var d uint32
for k := 0; k < 4; k++ {
d = d<<8 | uint32(salt[j])&0x000000FF
j++
if j >= len(salt) {
j = 0
}
}
expandedSalt[i] = d
}
var l, r uint32
for i := 0; i < 18; i += 2 {
l ^= expandedSalt[i&2]
r ^= expandedSalt[(i&2)+1]
l, r = encryptBlock(l, r, c)
c.p[i], c.p[i+1] = l, r
}
for i := 0; i < 256; i += 4 {
l ^= expandedSalt[2]
r ^= expandedSalt[3]
l, r = encryptBlock(l, r, c)
c.s0[i], c.s0[i+1] = l, r
l ^= expandedSalt[0]
r ^= expandedSalt[1]
l, r = encryptBlock(l, r, c)
c.s0[i+2], c.s0[i+3] = l, r
}
for i := 0; i < 256; i += 4 {
l ^= expandedSalt[2]
r ^= expandedSalt[3]
l, r = encryptBlock(l, r, c)
c.s1[i], c.s1[i+1] = l, r
l ^= expandedSalt[0]
r ^= expandedSalt[1]
l, r = encryptBlock(l, r, c)
c.s1[i+2], c.s1[i+3] = l, r
}
for i := 0; i < 256; i += 4 {
l ^= expandedSalt[2]
r ^= expandedSalt[3]
l, r = encryptBlock(l, r, c)
c.s2[i], c.s2[i+1] = l, r
l ^= expandedSalt[0]
r ^= expandedSalt[1]
l, r = encryptBlock(l, r, c)
c.s2[i+2], c.s2[i+3] = l, r
}
for i := 0; i < 256; i += 4 {
l ^= expandedSalt[2]
r ^= expandedSalt[3]
l, r = encryptBlock(l, r, c)
c.s3[i], c.s3[i+1] = l, r
l ^= expandedSalt[0]
r ^= expandedSalt[1]
l, r = encryptBlock(l, r, c)
c.s3[i+2], c.s3[i+3] = l, r
}
}
func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) { func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
xl, xr := l, r xl, xr := l, r
xl ^= c.p[0] xl ^= c.p[0]
......
...@@ -190,3 +190,21 @@ func TestCipherDecrypt(t *testing.T) { ...@@ -190,3 +190,21 @@ func TestCipherDecrypt(t *testing.T) {
} }
} }
} }
func TestSaltedCipherKeyLength(t *testing.T) {
var key []byte
for i := 0; i < 4; i++ {
_, err := NewSaltedCipher(key, []byte{'a'})
if err != KeySizeError(i) {
t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(i))
}
key = append(key, 'a')
}
// A 57-byte key. One over the typical blowfish restriction.
key = []byte("012345678901234567890123456789012345678901234567890123456")
_, err := NewSaltedCipher(key, []byte{'a'})
if err != nil {
t.Errorf("NewSaltedCipher with long key, gave error %#v", err)
}
}
...@@ -31,12 +31,28 @@ func (k KeySizeError) String() string { ...@@ -31,12 +31,28 @@ func (k KeySizeError) String() string {
// NewCipher creates and returns a Cipher. // NewCipher creates and returns a Cipher.
// The key argument should be the Blowfish key, 4 to 56 bytes. // The key argument should be the Blowfish key, 4 to 56 bytes.
func NewCipher(key []byte) (*Cipher, os.Error) { func NewCipher(key []byte) (*Cipher, os.Error) {
var result Cipher
k := len(key) k := len(key)
if k < 4 || k > 56 { if k < 4 || k > 56 {
return nil, KeySizeError(k) return nil, KeySizeError(k)
} }
initCipher(key, &result)
ExpandKey(key, &result)
return &result, nil
}
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
// sufficient and desirable. For bcrypt compatiblity, the key can be over 56
// bytes.
func NewSaltedCipher(key, salt []byte) (*Cipher, os.Error) {
var result Cipher var result Cipher
expandKey(key, &result) k := len(key)
if k < 4 {
return nil, KeySizeError(k)
}
initCipher(key, &result)
expandKeyWithSalt(key, salt, &result)
return &result, nil return &result, nil
} }
...@@ -77,3 +93,11 @@ func (c *Cipher) Reset() { ...@@ -77,3 +93,11 @@ func (c *Cipher) Reset() {
zero(c.s2[0:]) zero(c.s2[0:])
zero(c.s3[0:]) zero(c.s3[0:])
} }
func initCipher(key []byte, c *Cipher) {
copy(c.p[0:], p[0:])
copy(c.s0[0:], s0[0:])
copy(c.s1[0:], s1[0:])
copy(c.s2[0:], s2[0:])
copy(c.s3[0:], s3[0:])
}
...@@ -295,7 +295,7 @@ func TestBaseMult(t *testing.T) { ...@@ -295,7 +295,7 @@ func TestBaseMult(t *testing.T) {
} }
x, y := p224.ScalarBaseMult(k.Bytes()) x, y := p224.ScalarBaseMult(k.Bytes())
if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y { if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y {
t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%s, %s)", i, e.k, x, y, e.x, e.y) t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%x, %s)", i, e.k, x, y, e.x, e.y)
} }
if testing.Short() && i > 5 { if testing.Short() && i > 5 {
break break
......
...@@ -15,7 +15,7 @@ func TestOCSPDecode(t *testing.T) { ...@@ -15,7 +15,7 @@ func TestOCSPDecode(t *testing.T) {
t.Error(err) t.Error(err)
} }
expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}} expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, ZoneOffset: 0, Zone: "UTC"}}
if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) { if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) {
t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate) t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate)
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build darwin freebsd linux openbsd
// Unix cryptographically secure pseudorandom number // Unix cryptographically secure pseudorandom number
// generator. // generator.
......
...@@ -7,8 +7,10 @@ package tls ...@@ -7,8 +7,10 @@ package tls
import ( import (
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/des"
"crypto/hmac" "crypto/hmac"
"crypto/rc4" "crypto/rc4"
"crypto/sha1"
"crypto/x509" "crypto/x509"
"hash" "hash"
"os" "os"
...@@ -23,7 +25,7 @@ type keyAgreement interface { ...@@ -23,7 +25,7 @@ type keyAgreement interface {
// ServerKeyExchange message, generateServerKeyExchange can return nil, // ServerKeyExchange message, generateServerKeyExchange can return nil,
// nil. // nil.
generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, os.Error) generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, os.Error)
processClientKeyExchange(*Config, *clientKeyExchangeMsg) ([]byte, os.Error) processClientKeyExchange(*Config, *clientKeyExchangeMsg, uint16) ([]byte, os.Error)
// On the client side, the next two methods are called in order. // On the client side, the next two methods are called in order.
...@@ -46,14 +48,16 @@ type cipherSuite struct { ...@@ -46,14 +48,16 @@ type cipherSuite struct {
// and point format that we can handle. // and point format that we can handle.
elliptic bool elliptic bool
cipher func(key, iv []byte, isRead bool) interface{} cipher func(key, iv []byte, isRead bool) interface{}
mac func(macKey []byte) hash.Hash mac func(version uint16, macKey []byte) macFunction
} }
var cipherSuites = map[uint16]*cipherSuite{ var cipherSuites = map[uint16]*cipherSuite{
TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, hmacSHA1}, TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, macSHA1},
TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, hmacSHA1}, TLS_RSA_WITH_3DES_EDE_CBC_SHA: &cipherSuite{24, 20, 8, rsaKA, false, cipher3DES, macSHA1},
TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, hmacSHA1}, TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, macSHA1},
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, hmacSHA1}, TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1},
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: &cipherSuite{24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1},
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
} }
func cipherRC4(key, iv []byte, isRead bool) interface{} { func cipherRC4(key, iv []byte, isRead bool) interface{} {
...@@ -61,6 +65,14 @@ func cipherRC4(key, iv []byte, isRead bool) interface{} { ...@@ -61,6 +65,14 @@ func cipherRC4(key, iv []byte, isRead bool) interface{} {
return cipher return cipher
} }
func cipher3DES(key, iv []byte, isRead bool) interface{} {
block, _ := des.NewTripleDESCipher(key)
if isRead {
return cipher.NewCBCDecrypter(block, iv)
}
return cipher.NewCBCEncrypter(block, iv)
}
func cipherAES(key, iv []byte, isRead bool) interface{} { func cipherAES(key, iv []byte, isRead bool) interface{} {
block, _ := aes.NewCipher(key) block, _ := aes.NewCipher(key)
if isRead { if isRead {
...@@ -69,8 +81,75 @@ func cipherAES(key, iv []byte, isRead bool) interface{} { ...@@ -69,8 +81,75 @@ func cipherAES(key, iv []byte, isRead bool) interface{} {
return cipher.NewCBCEncrypter(block, iv) return cipher.NewCBCEncrypter(block, iv)
} }
func hmacSHA1(key []byte) hash.Hash { // macSHA1 returns a macFunction for the given protocol version.
return hmac.NewSHA1(key) func macSHA1(version uint16, key []byte) macFunction {
if version == versionSSL30 {
mac := ssl30MAC{
h: sha1.New(),
key: make([]byte, len(key)),
}
copy(mac.key, key)
return mac
}
return tls10MAC{hmac.NewSHA1(key)}
}
type macFunction interface {
Size() int
MAC(seq, data []byte) []byte
}
// ssl30MAC implements the SSLv3 MAC function, as defined in
// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1
type ssl30MAC struct {
h hash.Hash
key []byte
}
func (s ssl30MAC) Size() int {
return s.h.Size()
}
var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36}
var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c}
func (s ssl30MAC) MAC(seq, record []byte) []byte {
padLength := 48
if s.h.Size() == 20 {
padLength = 40
}
s.h.Reset()
s.h.Write(s.key)
s.h.Write(ssl30Pad1[:padLength])
s.h.Write(seq)
s.h.Write(record[:1])
s.h.Write(record[3:5])
s.h.Write(record[recordHeaderLen:])
digest := s.h.Sum()
s.h.Reset()
s.h.Write(s.key)
s.h.Write(ssl30Pad2[:padLength])
s.h.Write(digest)
return s.h.Sum()
}
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3.
type tls10MAC struct {
h hash.Hash
}
func (s tls10MAC) Size() int {
return s.h.Size()
}
func (s tls10MAC) MAC(seq, record []byte) []byte {
s.h.Reset()
s.h.Write(seq)
s.h.Write(record)
return s.h.Sum()
} }
func rsaKA() keyAgreement { func rsaKA() keyAgreement {
...@@ -95,8 +174,10 @@ func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint1 ...@@ -95,8 +174,10 @@ func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint1
// A list of the possible cipher suite ids. Taken from // A list of the possible cipher suite ids. Taken from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
const ( const (
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
) )
...@@ -9,7 +9,7 @@ import ( ...@@ -9,7 +9,7 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"io" "io"
"io/ioutil" "strings"
"sync" "sync"
"time" "time"
) )
...@@ -20,8 +20,11 @@ const ( ...@@ -20,8 +20,11 @@ const (
recordHeaderLen = 5 // record header length recordHeaderLen = 5 // record header length
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
minVersion = 0x0301 // minimum supported version - TLS 1.0 versionSSL30 = 0x0300
maxVersion = 0x0301 // maximum supported version - TLS 1.0 versionTLS10 = 0x0301
minVersion = versionSSL30
maxVersion = versionTLS10
) )
// TLS record types. // TLS record types.
...@@ -98,6 +101,10 @@ type ConnectionState struct { ...@@ -98,6 +101,10 @@ type ConnectionState struct {
NegotiatedProtocol string NegotiatedProtocol string
NegotiatedProtocolIsMutual bool NegotiatedProtocolIsMutual bool
// ServerName contains the server name indicated by the client, if any.
// (Only valid for server connections.)
ServerName string
// the certificate chain that was presented by the other side // the certificate chain that was presented by the other side
PeerCertificates []*x509.Certificate PeerCertificates []*x509.Certificate
// the verified certificate chains built from PeerCertificates. // the verified certificate chains built from PeerCertificates.
...@@ -121,6 +128,14 @@ type Config struct { ...@@ -121,6 +128,14 @@ type Config struct {
// Server configurations must include at least one certificate. // Server configurations must include at least one certificate.
Certificates []Certificate Certificates []Certificate
// NameToCertificate maps from a certificate name to an element of
// Certificates. Note that a certificate name can be of the form
// '*.example.com' and so doesn't have to be a domain name as such.
// See Config.BuildNameToCertificate
// The nil value causes the first element of Certificates to be used
// for all connections.
NameToCertificate map[string]*Certificate
// RootCAs defines the set of root certificate authorities // RootCAs defines the set of root certificate authorities
// that clients use when verifying server certificates. // that clients use when verifying server certificates.
// If RootCAs is nil, TLS uses the host's root CA set. // If RootCAs is nil, TLS uses the host's root CA set.
...@@ -139,6 +154,14 @@ type Config struct { ...@@ -139,6 +154,14 @@ type Config struct {
// anything more than self-signed. // anything more than self-signed.
AuthenticateClient bool AuthenticateClient bool
// InsecureSkipVerify controls whether a client verifies the
// server's certificate chain and host name.
// If InsecureSkipVerify is true, TLS accepts any certificate
// presented by the server and any host name in that certificate.
// In this mode, TLS is susceptible to man-in-the-middle attacks.
// This should be used only for testing.
InsecureSkipVerify bool
// CipherSuites is a list of supported cipher suites. If CipherSuites // CipherSuites is a list of supported cipher suites. If CipherSuites
// is nil, TLS uses a list of suites supported by the implementation. // is nil, TLS uses a list of suites supported by the implementation.
CipherSuites []uint16 CipherSuites []uint16
...@@ -176,6 +199,59 @@ func (c *Config) cipherSuites() []uint16 { ...@@ -176,6 +199,59 @@ func (c *Config) cipherSuites() []uint16 {
return s return s
} }
// getCertificateForName returns the best certificate for the given name,
// defaulting to the first element of c.Certificates if there are no good
// options.
func (c *Config) getCertificateForName(name string) *Certificate {
if len(c.Certificates) == 1 || c.NameToCertificate == nil {
// There's only one choice, so no point doing any work.
return &c.Certificates[0]
}
name = strings.ToLower(name)
for len(name) > 0 && name[len(name)-1] == '.' {
name = name[:len(name)-1]
}
if cert, ok := c.NameToCertificate[name]; ok {
return cert
}
// try replacing labels in the name with wildcards until we get a
// match.
labels := strings.Split(name, ".")
for i := range labels {
labels[i] = "*"
candidate := strings.Join(labels, ".")
if cert, ok := c.NameToCertificate[candidate]; ok {
return cert
}
}
// If nothing matches, return the first certificate.
return &c.Certificates[0]
}
// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
// from the CommonName and SubjectAlternateName fields of each of the leaf
// certificates.
func (c *Config) BuildNameToCertificate() {
c.NameToCertificate = make(map[string]*Certificate)
for i := range c.Certificates {
cert := &c.Certificates[i]
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
continue
}
if len(x509Cert.Subject.CommonName) > 0 {
c.NameToCertificate[x509Cert.Subject.CommonName] = cert
}
for _, san := range x509Cert.DNSNames {
c.NameToCertificate[san] = cert
}
}
}
// A Certificate is a chain of one or more certificates, leaf first. // A Certificate is a chain of one or more certificates, leaf first.
type Certificate struct { type Certificate struct {
Certificate [][]byte Certificate [][]byte
...@@ -215,15 +291,6 @@ func defaultConfig() *Config { ...@@ -215,15 +291,6 @@ func defaultConfig() *Config {
return &emptyConfig return &emptyConfig
} }
// Possible certificate files; stop after finding one.
// On OS X we should really be using the Directory Services keychain
// but that requires a lot of Mach goo to get at. Instead we use
// the same root set that curl uses.
var certFiles = []string{
"/etc/ssl/certs/ca-certificates.crt", // Linux etc
"/usr/share/curl/curl-ca-bundle.crt", // OS X
}
var once sync.Once var once sync.Once
func defaultRoots() *x509.CertPool { func defaultRoots() *x509.CertPool {
...@@ -241,21 +308,10 @@ func initDefaults() { ...@@ -241,21 +308,10 @@ func initDefaults() {
initDefaultCipherSuites() initDefaultCipherSuites()
} }
var varDefaultRoots *x509.CertPool var (
varDefaultRoots *x509.CertPool
func initDefaultRoots() { varDefaultCipherSuites []uint16
roots := x509.NewCertPool() )
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
break
}
}
varDefaultRoots = roots
}
var varDefaultCipherSuites []uint16
func initDefaultCipherSuites() { func initDefaultCipherSuites() {
varDefaultCipherSuites = make([]uint16, len(cipherSuites)) varDefaultCipherSuites = make([]uint16, len(cipherSuites))
......
...@@ -11,7 +11,6 @@ import ( ...@@ -11,7 +11,6 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/subtle" "crypto/subtle"
"crypto/x509" "crypto/x509"
"hash"
"io" "io"
"net" "net"
"os" "os"
...@@ -37,6 +36,8 @@ type Conn struct { ...@@ -37,6 +36,8 @@ type Conn struct {
// verifiedChains contains the certificate chains that we built, as // verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server. // opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate verifiedChains [][]*x509.Certificate
// serverName contains the server name indicated by the client, if any.
serverName string
clientProtocol string clientProtocol string
clientProtocolFallback bool clientProtocolFallback bool
...@@ -108,18 +109,20 @@ func (c *Conn) SetWriteTimeout(nsec int64) os.Error { ...@@ -108,18 +109,20 @@ func (c *Conn) SetWriteTimeout(nsec int64) os.Error {
// connection, either sending or receiving. // connection, either sending or receiving.
type halfConn struct { type halfConn struct {
sync.Mutex sync.Mutex
cipher interface{} // cipher algorithm version uint16 // protocol version
mac hash.Hash // MAC algorithm cipher interface{} // cipher algorithm
seq [8]byte // 64-bit sequence number mac macFunction
bfree *block // list of free blocks seq [8]byte // 64-bit sequence number
bfree *block // list of free blocks
nextCipher interface{} // next encryption state nextCipher interface{} // next encryption state
nextMac hash.Hash // next MAC algorithm nextMac macFunction // next MAC algorithm
} }
// prepareCipherSpec sets the encryption and MAC states // prepareCipherSpec sets the encryption and MAC states
// that a subsequent changeCipherSpec will use. // that a subsequent changeCipherSpec will use.
func (hc *halfConn) prepareCipherSpec(cipher interface{}, mac hash.Hash) { func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) {
hc.version = version
hc.nextCipher = cipher hc.nextCipher = cipher
hc.nextMac = mac hc.nextMac = mac
} }
...@@ -197,6 +200,22 @@ func removePadding(payload []byte) ([]byte, byte) { ...@@ -197,6 +200,22 @@ func removePadding(payload []byte) ([]byte, byte) {
return payload[:len(payload)-int(toRemove)], good return payload[:len(payload)-int(toRemove)], good
} }
// removePaddingSSL30 is a replacement for removePadding in the case that the
// protocol version is SSLv3. In this version, the contents of the padding
// are random and cannot be checked.
func removePaddingSSL30(payload []byte) ([]byte, byte) {
if len(payload) < 1 {
return payload, 0
}
paddingLen := int(payload[len(payload)-1]) + 1
if paddingLen > len(payload) {
return payload, 0
}
return payload[:len(payload)-paddingLen], 255
}
func roundUp(a, b int) int { func roundUp(a, b int) int {
return a + (b-a%b)%b return a + (b-a%b)%b
} }
...@@ -226,7 +245,11 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { ...@@ -226,7 +245,11 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) {
} }
c.CryptBlocks(payload, payload) c.CryptBlocks(payload, payload)
payload, paddingGood = removePadding(payload) if hc.version == versionSSL30 {
payload, paddingGood = removePaddingSSL30(payload)
} else {
payload, paddingGood = removePadding(payload)
}
b.resize(recordHeaderLen + len(payload)) b.resize(recordHeaderLen + len(payload))
// note that we still have a timing side-channel in the // note that we still have a timing side-channel in the
...@@ -256,13 +279,10 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { ...@@ -256,13 +279,10 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) {
b.data[4] = byte(n) b.data[4] = byte(n)
b.resize(recordHeaderLen + n) b.resize(recordHeaderLen + n)
remoteMAC := payload[n:] remoteMAC := payload[n:]
localMAC := hc.mac.MAC(hc.seq[0:], b.data)
hc.mac.Reset()
hc.mac.Write(hc.seq[0:])
hc.incSeq() hc.incSeq()
hc.mac.Write(b.data)
if subtle.ConstantTimeCompare(hc.mac.Sum(), remoteMAC) != 1 || paddingGood != 255 { if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 {
return false, alertBadRecordMAC return false, alertBadRecordMAC
} }
} }
...@@ -291,11 +311,9 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) { ...@@ -291,11 +311,9 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) {
func (hc *halfConn) encrypt(b *block) (bool, alert) { func (hc *halfConn) encrypt(b *block) (bool, alert) {
// mac // mac
if hc.mac != nil { if hc.mac != nil {
hc.mac.Reset() mac := hc.mac.MAC(hc.seq[0:], b.data)
hc.mac.Write(hc.seq[0:])
hc.incSeq() hc.incSeq()
hc.mac.Write(b.data)
mac := hc.mac.Sum()
n := len(b.data) n := len(b.data)
b.resize(n + len(mac)) b.resize(n + len(mac))
copy(b.data[n:], mac) copy(b.data[n:], mac)
...@@ -470,6 +488,19 @@ Again: ...@@ -470,6 +488,19 @@ Again:
if n > maxCiphertext { if n > maxCiphertext {
return c.sendAlert(alertRecordOverflow) return c.sendAlert(alertRecordOverflow)
} }
if !c.haveVers {
// First message, be extra suspicious:
// this might not be a TLS client.
// Bail out before reading a full 'body', if possible.
// The current max version is 3.1.
// If the version is >= 16.0, it's probably not real.
// Similarly, a clientHello message encodes in
// well under a kilobyte. If the length is >= 12 kB,
// it's probably not real.
if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 {
return c.sendAlert(alertUnexpectedMessage)
}
}
if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil { if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
if err == os.EOF { if err == os.EOF {
err = io.ErrUnexpectedEOF err = io.ErrUnexpectedEOF
...@@ -627,7 +658,9 @@ func (c *Conn) readHandshake() (interface{}, os.Error) { ...@@ -627,7 +658,9 @@ func (c *Conn) readHandshake() (interface{}, os.Error) {
if c.err != nil { if c.err != nil {
return nil, c.err return nil, c.err
} }
c.readRecord(recordTypeHandshake) if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err
}
} }
data := c.hand.Bytes() data := c.hand.Bytes()
...@@ -640,7 +673,9 @@ func (c *Conn) readHandshake() (interface{}, os.Error) { ...@@ -640,7 +673,9 @@ func (c *Conn) readHandshake() (interface{}, os.Error) {
if c.err != nil { if c.err != nil {
return nil, c.err return nil, c.err
} }
c.readRecord(recordTypeHandshake) if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err
}
} }
data = c.hand.Next(4 + n) data = c.hand.Next(4 + n)
var m handshakeMessage var m handshakeMessage
...@@ -731,10 +766,18 @@ func (c *Conn) Read(b []byte) (n int, err os.Error) { ...@@ -731,10 +766,18 @@ func (c *Conn) Read(b []byte) (n int, err os.Error) {
// Close closes the connection. // Close closes the connection.
func (c *Conn) Close() os.Error { func (c *Conn) Close() os.Error {
if err := c.Handshake(); err != nil { var alertErr os.Error
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
if c.handshakeComplete {
alertErr = c.sendAlert(alertCloseNotify)
}
if err := c.conn.Close(); err != nil {
return err return err
} }
return c.sendAlert(alertCloseNotify) return alertErr
} }
// Handshake runs the client or server handshake // Handshake runs the client or server handshake
...@@ -769,6 +812,7 @@ func (c *Conn) ConnectionState() ConnectionState { ...@@ -769,6 +812,7 @@ func (c *Conn) ConnectionState() ConnectionState {
state.CipherSuite = c.cipherSuite state.CipherSuite = c.cipherSuite
state.PeerCertificates = c.peerCertificates state.PeerCertificates = c.peerCertificates
state.VerifiedChains = c.verifiedChains state.VerifiedChains = c.verifiedChains
state.ServerName = c.serverName
} }
return state return state
......
...@@ -50,3 +50,57 @@ func TestRemovePadding(t *testing.T) { ...@@ -50,3 +50,57 @@ func TestRemovePadding(t *testing.T) {
} }
} }
} }
var certExampleCom = `308201403081eda003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313138353835325a170d3132303933303138353835325a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31a301830160603551d11040f300d820b6578616d706c652e636f6d300b06092a864886f70d0101050341001a0b419d2c74474c6450654e5f10b32bf426ffdf55cad1c52602e7a9151513a3424c70f5960dcd682db0c33769cc1daa3fcdd3db10809d2392ed4a1bf50ced18`
var certWildcardExampleCom = `308201423081efa003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303034365a170d3132303933303139303034365a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31c301a30180603551d110411300f820d2a2e6578616d706c652e636f6d300b06092a864886f70d0101050341001676f0c9e7c33c1b656ed5a6476c4e2ee9ec8e62df7407accb1875272b2edd0a22096cb2c22598d11604104d604f810eb4b5987ca6bb319c7e6ce48725c54059`
var certFooExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303131345a170d3132303933303139303131345a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104133011820f666f6f2e6578616d706c652e636f6d300b06092a864886f70d010105034100646a2a51f2aa2477add854b462cf5207ba16d3213ffb5d3d0eed473fbf09935019192d1d5b8ca6a2407b424cf04d97c4cd9197c83ecf81f0eab9464a1109d09f`
var certDoubleWildcardExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303134315a170d3132303933303139303134315a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104133011820f2a2e2a2e6578616d706c652e636f6d300b06092a864886f70d0101050341001c3de267975f56ef57771c6218ef95ecc65102e57bd1defe6f7efea90d9b26cf40de5bd7ad75e46201c7f2a92aaa3e907451e9409f65e28ddb6db80d726290f6`
func TestCertificateSelection(t *testing.T) {
config := Config{
Certificates: []Certificate{
{
Certificate: [][]byte{fromHex(certExampleCom)},
},
{
Certificate: [][]byte{fromHex(certWildcardExampleCom)},
},
{
Certificate: [][]byte{fromHex(certFooExampleCom)},
},
{
Certificate: [][]byte{fromHex(certDoubleWildcardExampleCom)},
},
},
}
config.BuildNameToCertificate()
pointerToIndex := func(c *Certificate) int {
for i := range config.Certificates {
if c == &config.Certificates[i] {
return i
}
}
return -1
}
if n := pointerToIndex(config.getCertificateForName("example.com")); n != 0 {
t.Errorf("example.com returned certificate %d, not 0", n)
}
if n := pointerToIndex(config.getCertificateForName("bar.example.com")); n != 1 {
t.Errorf("bar.example.com returned certificate %d, not 1", n)
}
if n := pointerToIndex(config.getCertificateForName("foo.example.com")); n != 2 {
t.Errorf("foo.example.com returned certificate %d, not 2", n)
}
if n := pointerToIndex(config.getCertificateForName("foo.bar.example.com")); n != 3 {
t.Errorf("foo.bar.example.com returned certificate %d, not 3", n)
}
if n := pointerToIndex(config.getCertificateForName("foo.bar.baz.example.com")); n != 0 {
t.Errorf("foo.bar.baz.example.com returned certificate %d, not 0", n)
}
}
...@@ -14,7 +14,7 @@ import ( ...@@ -14,7 +14,7 @@ import (
) )
func (c *Conn) clientHandshake() os.Error { func (c *Conn) clientHandshake() os.Error {
finishedHash := newFinishedHash() finishedHash := newFinishedHash(versionTLS10)
if c.config == nil { if c.config == nil {
c.config = defaultConfig() c.config = defaultConfig()
...@@ -97,11 +97,9 @@ func (c *Conn) clientHandshake() os.Error { ...@@ -97,11 +97,9 @@ func (c *Conn) clientHandshake() os.Error {
certs[i] = cert certs[i] = cert
} }
// If we don't have a root CA set configured then anything is accepted. if !c.config.InsecureSkipVerify {
// TODO(rsc): Find certificates for OS X 10.6.
if c.config.RootCAs != nil {
opts := x509.VerifyOptions{ opts := x509.VerifyOptions{
Roots: c.config.RootCAs, Roots: c.config.rootCAs(),
CurrentTime: c.config.time(), CurrentTime: c.config.time(),
DNSName: c.config.ServerName, DNSName: c.config.ServerName,
Intermediates: x509.NewCertPool(), Intermediates: x509.NewCertPool(),
...@@ -247,11 +245,11 @@ func (c *Conn) clientHandshake() os.Error { ...@@ -247,11 +245,11 @@ func (c *Conn) clientHandshake() os.Error {
} }
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromPreMasterSecret10(preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */ ) clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */ )
clientHash := suite.mac(clientMAC) clientHash := suite.mac(c.vers, clientMAC)
c.out.prepareCipherSpec(clientCipher, clientHash) c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
if serverHello.nextProtoNeg { if serverHello.nextProtoNeg {
...@@ -271,8 +269,8 @@ func (c *Conn) clientHandshake() os.Error { ...@@ -271,8 +269,8 @@ func (c *Conn) clientHandshake() os.Error {
c.writeRecord(recordTypeHandshake, finished.marshal()) c.writeRecord(recordTypeHandshake, finished.marshal())
serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */ ) serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */ )
serverHash := suite.mac(serverMAC) serverHash := suite.mac(c.vers, serverMAC)
c.in.prepareCipherSpec(serverCipher, serverHash) c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.readRecord(recordTypeChangeCipherSpec) c.readRecord(recordTypeChangeCipherSpec)
if c.err != nil { if c.err != nil {
return c.err return c.err
......
...@@ -18,6 +18,7 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config * ...@@ -18,6 +18,7 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config *
go func() { go func() {
cli.Write([]byte("hello\n")) cli.Write([]byte("hello\n"))
cli.Close() cli.Close()
c.Close()
}() }()
defer c.Close() defer c.Close()
......
...@@ -676,9 +676,9 @@ func (m *finishedMsg) marshal() (x []byte) { ...@@ -676,9 +676,9 @@ func (m *finishedMsg) marshal() (x []byte) {
return m.raw return m.raw
} }
x = make([]byte, 16) x = make([]byte, 4+len(m.verifyData))
x[0] = typeFinished x[0] = typeFinished
x[3] = 12 x[3] = byte(len(m.verifyData))
copy(x[4:], m.verifyData) copy(x[4:], m.verifyData)
m.raw = x m.raw = x
return return
...@@ -686,7 +686,7 @@ func (m *finishedMsg) marshal() (x []byte) { ...@@ -686,7 +686,7 @@ func (m *finishedMsg) marshal() (x []byte) {
func (m *finishedMsg) unmarshal(data []byte) bool { func (m *finishedMsg) unmarshal(data []byte) bool {
m.raw = data m.raw = data
if len(data) != 4+12 { if len(data) < 4 {
return false return false
} }
m.verifyData = data[4:] m.verifyData = data[4:]
......
...@@ -14,13 +14,13 @@ import ( ...@@ -14,13 +14,13 @@ import (
var tests = []interface{}{ var tests = []interface{}{
&clientHelloMsg{}, &clientHelloMsg{},
&serverHelloMsg{}, &serverHelloMsg{},
&finishedMsg{},
&certificateMsg{}, &certificateMsg{},
&certificateRequestMsg{}, &certificateRequestMsg{},
&certificateVerifyMsg{}, &certificateVerifyMsg{},
&certificateStatusMsg{}, &certificateStatusMsg{},
&clientKeyExchangeMsg{}, &clientKeyExchangeMsg{},
&finishedMsg{},
&nextProtoMsg{}, &nextProtoMsg{},
} }
...@@ -59,11 +59,12 @@ func TestMarshalUnmarshal(t *testing.T) { ...@@ -59,11 +59,12 @@ func TestMarshalUnmarshal(t *testing.T) {
break break
} }
if i >= 2 { if i >= 3 {
// The first two message types (ClientHello and // The first three message types (ClientHello,
// ServerHello) are allowed to have parsable // ServerHello and Finished) are allowed to
// prefixes because the extension data is // have parsable prefixes because the extension
// optional. // data is optional and the length of the
// Finished varies across versions.
for j := 0; j < len(marshaled); j++ { for j := 0; j < len(marshaled); j++ {
if m2.unmarshal(marshaled[0:j]) { if m2.unmarshal(marshaled[0:j]) {
t.Errorf("#%d unmarshaled a prefix of length %d of %#v", i, j, m1) t.Errorf("#%d unmarshaled a prefix of length %d of %#v", i, j, m1)
......
...@@ -30,7 +30,7 @@ func (c *Conn) serverHandshake() os.Error { ...@@ -30,7 +30,7 @@ func (c *Conn) serverHandshake() os.Error {
c.vers = vers c.vers = vers
c.haveVers = true c.haveVers = true
finishedHash := newFinishedHash() finishedHash := newFinishedHash(vers)
finishedHash.Write(clientHello.marshal()) finishedHash.Write(clientHello.marshal())
hello := new(serverHelloMsg) hello := new(serverHelloMsg)
...@@ -115,7 +115,12 @@ FindCipherSuite: ...@@ -115,7 +115,12 @@ FindCipherSuite:
} }
certMsg := new(certificateMsg) certMsg := new(certificateMsg)
certMsg.certificates = config.Certificates[0].Certificate if len(clientHello.serverName) > 0 {
c.serverName = clientHello.serverName
certMsg.certificates = config.getCertificateForName(clientHello.serverName).Certificate
} else {
certMsg.certificates = config.Certificates[0].Certificate
}
finishedHash.Write(certMsg.marshal()) finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal())
...@@ -128,7 +133,6 @@ FindCipherSuite: ...@@ -128,7 +133,6 @@ FindCipherSuite:
} }
keyAgreement := suite.ka() keyAgreement := suite.ka()
skx, err := keyAgreement.generateServerKeyExchange(config, clientHello, hello) skx, err := keyAgreement.generateServerKeyExchange(config, clientHello, hello)
if err != nil { if err != nil {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
...@@ -235,18 +239,18 @@ FindCipherSuite: ...@@ -235,18 +239,18 @@ FindCipherSuite:
finishedHash.Write(certVerify.marshal()) finishedHash.Write(certVerify.marshal())
} }
preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx) preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx, c.vers)
if err != nil { if err != nil {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
} }
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromPreMasterSecret10(preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen)
clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */ ) clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */ )
clientHash := suite.mac(clientMAC) clientHash := suite.mac(c.vers, clientMAC)
c.in.prepareCipherSpec(clientCipher, clientHash) c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.readRecord(recordTypeChangeCipherSpec) c.readRecord(recordTypeChangeCipherSpec)
if err := c.error(); err != nil { if err := c.error(); err != nil {
return err return err
...@@ -283,8 +287,8 @@ FindCipherSuite: ...@@ -283,8 +287,8 @@ FindCipherSuite:
finishedHash.Write(clientFinished.marshal()) finishedHash.Write(clientFinished.marshal())
serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */ ) serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */ )
serverHash := suite.mac(serverMAC) serverHash := suite.mac(c.vers, serverMAC)
c.out.prepareCipherSpec(serverCipher, serverHash) c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
finished := new(finishedMsg) finished := new(finishedMsg)
......
...@@ -24,7 +24,7 @@ func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello ...@@ -24,7 +24,7 @@ func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello
return nil, nil return nil, nil
} }
func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, os.Error) {
preMasterSecret := make([]byte, 48) preMasterSecret := make([]byte, 48)
_, err := io.ReadFull(config.rand(), preMasterSecret[2:]) _, err := io.ReadFull(config.rand(), preMasterSecret[2:])
if err != nil { if err != nil {
...@@ -34,11 +34,15 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe ...@@ -34,11 +34,15 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe
if len(ckx.ciphertext) < 2 { if len(ckx.ciphertext) < 2 {
return nil, os.NewError("bad ClientKeyExchange") return nil, os.NewError("bad ClientKeyExchange")
} }
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
if ciphertextLen != len(ckx.ciphertext)-2 { ciphertext := ckx.ciphertext
return nil, os.NewError("bad ClientKeyExchange") if version != versionSSL30 {
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
if ciphertextLen != len(ckx.ciphertext)-2 {
return nil, os.NewError("bad ClientKeyExchange")
}
ciphertext = ckx.ciphertext[2:]
} }
ciphertext := ckx.ciphertext[2:]
err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret) err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret)
if err != nil { if err != nil {
...@@ -159,7 +163,7 @@ Curve: ...@@ -159,7 +163,7 @@ Curve:
return skx, nil return skx, nil
} }
func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) { func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, os.Error) {
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
return nil, os.NewError("bad ClientKeyExchange") return nil, os.NewError("bad ClientKeyExchange")
} }
......
...@@ -63,6 +63,39 @@ func pRF10(result, secret, label, seed []byte) { ...@@ -63,6 +63,39 @@ func pRF10(result, secret, label, seed []byte) {
} }
} }
// pRF30 implements the SSL 3.0 pseudo-random function, as defined in
// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6.
func pRF30(result, secret, label, seed []byte) {
hashSHA1 := sha1.New()
hashMD5 := md5.New()
done := 0
i := 0
// RFC5246 section 6.3 says that the largest PRF output needed is 128
// bytes. Since no more ciphersuites will be added to SSLv3, this will
// remain true. Each iteration gives us 16 bytes so 10 iterations will
// be sufficient.
var b [11]byte
for done < len(result) {
for j := 0; j <= i; j++ {
b[j] = 'A' + byte(i)
}
hashSHA1.Reset()
hashSHA1.Write(b[:i+1])
hashSHA1.Write(secret)
hashSHA1.Write(seed)
digest := hashSHA1.Sum()
hashMD5.Reset()
hashMD5.Write(secret)
hashMD5.Write(digest)
done += copy(result[done:], hashMD5.Sum())
i++
}
}
const ( const (
tlsRandomLength = 32 // Length of a random nonce in TLS 1.1. tlsRandomLength = 32 // Length of a random nonce in TLS 1.1.
masterSecretLength = 48 // Length of a master secret in TLS 1.1. masterSecretLength = 48 // Length of a master secret in TLS 1.1.
...@@ -77,19 +110,24 @@ var serverFinishedLabel = []byte("server finished") ...@@ -77,19 +110,24 @@ var serverFinishedLabel = []byte("server finished")
// keysFromPreMasterSecret generates the connection keys from the pre master // keysFromPreMasterSecret generates the connection keys from the pre master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in // secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, section 6.3. // RFC 2246, section 6.3.
func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
prf := pRF10
if version == versionSSL30 {
prf = pRF30
}
var seed [tlsRandomLength * 2]byte var seed [tlsRandomLength * 2]byte
copy(seed[0:len(clientRandom)], clientRandom) copy(seed[0:len(clientRandom)], clientRandom)
copy(seed[len(clientRandom):], serverRandom) copy(seed[len(clientRandom):], serverRandom)
masterSecret = make([]byte, masterSecretLength) masterSecret = make([]byte, masterSecretLength)
pRF10(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
copy(seed[0:len(clientRandom)], serverRandom) copy(seed[0:len(clientRandom)], serverRandom)
copy(seed[len(serverRandom):], clientRandom) copy(seed[len(serverRandom):], clientRandom)
n := 2*macLen + 2*keyLen + 2*ivLen n := 2*macLen + 2*keyLen + 2*ivLen
keyMaterial := make([]byte, n) keyMaterial := make([]byte, n)
pRF10(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:])
clientMAC = keyMaterial[:macLen] clientMAC = keyMaterial[:macLen]
keyMaterial = keyMaterial[macLen:] keyMaterial = keyMaterial[macLen:]
serverMAC = keyMaterial[:macLen] serverMAC = keyMaterial[:macLen]
...@@ -104,6 +142,10 @@ func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byt ...@@ -104,6 +142,10 @@ func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byt
return return
} }
func newFinishedHash(version uint16) finishedHash {
return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version}
}
// A finishedHash calculates the hash of a set of handshake messages suitable // A finishedHash calculates the hash of a set of handshake messages suitable
// for including in a Finished message. // for including in a Finished message.
type finishedHash struct { type finishedHash struct {
...@@ -111,10 +153,7 @@ type finishedHash struct { ...@@ -111,10 +153,7 @@ type finishedHash struct {
clientSHA1 hash.Hash clientSHA1 hash.Hash
serverMD5 hash.Hash serverMD5 hash.Hash
serverSHA1 hash.Hash serverSHA1 hash.Hash
} version uint16
func newFinishedHash() finishedHash {
return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New()}
} }
func (h finishedHash) Write(msg []byte) (n int, err os.Error) { func (h finishedHash) Write(msg []byte) (n int, err os.Error) {
...@@ -125,9 +164,10 @@ func (h finishedHash) Write(msg []byte) (n int, err os.Error) { ...@@ -125,9 +164,10 @@ func (h finishedHash) Write(msg []byte) (n int, err os.Error) {
return len(msg), nil return len(msg), nil
} }
// finishedSum calculates the contents of the verify_data member of a Finished // finishedSum10 calculates the contents of the verify_data member of a TLSv1
// message given the MD5 and SHA1 hashes of a set of handshake messages. // Finished message given the MD5 and SHA1 hashes of a set of handshake
func finishedSum(md5, sha1, label, masterSecret []byte) []byte { // messages.
func finishedSum10(md5, sha1, label, masterSecret []byte) []byte {
seed := make([]byte, len(md5)+len(sha1)) seed := make([]byte, len(md5)+len(sha1))
copy(seed, md5) copy(seed, md5)
copy(seed[len(md5):], sha1) copy(seed[len(md5):], sha1)
...@@ -136,18 +176,61 @@ func finishedSum(md5, sha1, label, masterSecret []byte) []byte { ...@@ -136,18 +176,61 @@ func finishedSum(md5, sha1, label, masterSecret []byte) []byte {
return out return out
} }
// finishedSum30 calculates the contents of the verify_data member of a SSLv3
// Finished message given the MD5 and SHA1 hashes of a set of handshake
// messages.
func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []byte {
md5.Write(magic[:])
md5.Write(masterSecret)
md5.Write(ssl30Pad1[:])
md5Digest := md5.Sum()
md5.Reset()
md5.Write(masterSecret)
md5.Write(ssl30Pad2[:])
md5.Write(md5Digest)
md5Digest = md5.Sum()
sha1.Write(magic[:])
sha1.Write(masterSecret)
sha1.Write(ssl30Pad1[:40])
sha1Digest := sha1.Sum()
sha1.Reset()
sha1.Write(masterSecret)
sha1.Write(ssl30Pad2[:40])
sha1.Write(sha1Digest)
sha1Digest = sha1.Sum()
ret := make([]byte, len(md5Digest)+len(sha1Digest))
copy(ret, md5Digest)
copy(ret[len(md5Digest):], sha1Digest)
return ret
}
var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54}
var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52}
// clientSum returns the contents of the verify_data member of a client's // clientSum returns the contents of the verify_data member of a client's
// Finished message. // Finished message.
func (h finishedHash) clientSum(masterSecret []byte) []byte { func (h finishedHash) clientSum(masterSecret []byte) []byte {
if h.version == versionSSL30 {
return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic)
}
md5 := h.clientMD5.Sum() md5 := h.clientMD5.Sum()
sha1 := h.clientSHA1.Sum() sha1 := h.clientSHA1.Sum()
return finishedSum(md5, sha1, clientFinishedLabel, masterSecret) return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret)
} }
// serverSum returns the contents of the verify_data member of a server's // serverSum returns the contents of the verify_data member of a server's
// Finished message. // Finished message.
func (h finishedHash) serverSum(masterSecret []byte) []byte { func (h finishedHash) serverSum(masterSecret []byte) []byte {
if h.version == versionSSL30 {
return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic)
}
md5 := h.serverMD5.Sum() md5 := h.serverMD5.Sum()
sha1 := h.serverSHA1.Sum() sha1 := h.serverSHA1.Sum()
return finishedSum(md5, sha1, serverFinishedLabel, masterSecret) return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret)
} }
...@@ -34,6 +34,7 @@ func TestSplitPreMasterSecret(t *testing.T) { ...@@ -34,6 +34,7 @@ func TestSplitPreMasterSecret(t *testing.T) {
} }
type testKeysFromTest struct { type testKeysFromTest struct {
version uint16
preMasterSecret string preMasterSecret string
clientRandom, serverRandom string clientRandom, serverRandom string
masterSecret string masterSecret string
...@@ -47,7 +48,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { ...@@ -47,7 +48,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
in, _ := hex.DecodeString(test.preMasterSecret) in, _ := hex.DecodeString(test.preMasterSecret)
clientRandom, _ := hex.DecodeString(test.clientRandom) clientRandom, _ := hex.DecodeString(test.clientRandom)
serverRandom, _ := hex.DecodeString(test.serverRandom) serverRandom, _ := hex.DecodeString(test.serverRandom)
master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret10(in, clientRandom, serverRandom, test.macLen, test.keyLen, 0) master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret(test.version, in, clientRandom, serverRandom, test.macLen, test.keyLen, 0)
masterString := hex.EncodeToString(master) masterString := hex.EncodeToString(master)
clientMACString := hex.EncodeToString(clientMAC) clientMACString := hex.EncodeToString(clientMAC)
serverMACString := hex.EncodeToString(serverMAC) serverMACString := hex.EncodeToString(serverMAC)
...@@ -58,7 +59,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { ...@@ -58,7 +59,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
serverMACString != test.serverMAC || serverMACString != test.serverMAC ||
clientKeyString != test.clientKey || clientKeyString != test.clientKey ||
serverKeyString != test.serverKey { serverKeyString != test.serverKey {
t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverMACString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s, %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverKeyString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey)
} }
} }
} }
...@@ -66,6 +67,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { ...@@ -66,6 +67,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
// These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 ` // These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 `
var testKeysFromTests = []testKeysFromTest{ var testKeysFromTests = []testKeysFromTest{
{ {
versionTLS10,
"0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5", "0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5",
"4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558", "4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558",
"4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db", "4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db",
...@@ -78,6 +80,7 @@ var testKeysFromTests = []testKeysFromTest{ ...@@ -78,6 +80,7 @@ var testKeysFromTests = []testKeysFromTest{
16, 16,
}, },
{ {
versionTLS10,
"03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890", "03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890",
"4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106", "4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106",
"4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c", "4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c",
...@@ -90,6 +93,7 @@ var testKeysFromTests = []testKeysFromTest{ ...@@ -90,6 +93,7 @@ var testKeysFromTests = []testKeysFromTest{
16, 16,
}, },
{ {
versionTLS10,
"832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1",
"4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e",
"4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e",
...@@ -101,4 +105,17 @@ var testKeysFromTests = []testKeysFromTest{ ...@@ -101,4 +105,17 @@ var testKeysFromTests = []testKeysFromTest{
20, 20,
16, 16,
}, },
{
versionSSL30,
"832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1",
"4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e",
"4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e",
"a614863e56299dcffeea2938f22c2ba023768dbe4b3f6877bc9c346c6ae529b51d9cb87ff9695ea4d01f2205584405b2",
"2c450d5b6f6e2013ac6bea6a0b32200d4e1ffb94",
"7a7a7438769536f2fb1ae49a61f0703b79b2dc53",
"f8f6b26c10f12855c9aafb1e0e839ccf",
"2b9d4b4a60cb7f396780ebff50650419",
20,
16,
},
} }
// Copyright 2011 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 tls
/*
// Note: We disable -Werror here because the code in this file uses a deprecated API to stay
// compatible with both Mac OS X 10.6 and 10.7. Using a deprecated function on Darwin generates
// a warning.
#cgo CFLAGS: -Wno-error
#cgo LDFLAGS: -framework CoreFoundation -framework Security
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
// FetchPEMRoots fetches the system's list of trusted X.509 root certificates.
//
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
// certificates of the system. On failure, the function returns -1.
//
// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after
// we've consumed its content.
int FetchPEMRoots(CFDataRef *pemRoots) {
if (pemRoots == NULL) {
return -1;
}
CFArrayRef certs = NULL;
OSStatus err = SecTrustCopyAnchorCertificates(&certs);
if (err != noErr) {
return -1;
}
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
int i, ncerts = CFArrayGetCount(certs);
for (i = 0; i < ncerts; i++) {
CFDataRef data = NULL;
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
if (cert == NULL) {
continue;
}
// SecKeychainImportExport is deprecated in >= OS X 10.7, and has been replaced by
// SecItemExport. If we're built on a host with a Lion SDK, this code gets conditionally
// included in the output, also for binaries meant for 10.6.
//
// To make sure that we run on both Mac OS X 10.6 and 10.7 we use weak linking
// and check whether SecItemExport is available before we attempt to call it. On
// 10.6, this won't be the case, and we'll fall back to calling SecKeychainItemExport.
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
if (SecItemExport) {
err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
if (err != noErr) {
continue;
}
} else
#endif
if (data == NULL) {
err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
if (err != noErr) {
continue;
}
}
if (data != NULL) {
CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
CFRelease(data);
}
}
CFRelease(certs);
*pemRoots = combinedData;
return 0;
}
*/
import "C"
import (
"crypto/x509"
"unsafe"
)
func initDefaultRoots() {
roots := x509.NewCertPool()
var data C.CFDataRef = nil
err := C.FetchPEMRoots(&data)
if err != -1 {
defer C.CFRelease(C.CFTypeRef(data))
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
roots.AppendCertsFromPEM(buf)
}
varDefaultRoots = roots
}
// Copyright 2011 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 tls
func initDefaultRoots() {
}
// Copyright 2011 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 tls
import (
"testing"
)
var tlsServers = []string{
"google.com:443",
"github.com:443",
"twitter.com:443",
}
func TestOSCertBundles(t *testing.T) {
defaultRoots()
if testing.Short() {
t.Logf("skipping certificate tests in short mode")
return
}
for _, addr := range tlsServers {
conn, err := Dial("tcp", addr, nil)
if err != nil {
t.Errorf("unable to verify %v: %v", addr, err)
continue
}
err = conn.Close()
if err != nil {
t.Error(err)
}
}
}
// Copyright 2011 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 tls
import (
"crypto/x509"
"io/ioutil"
)
// Possible certificate files; stop after finding one.
var certFiles = []string{
"/etc/ssl/certs/ca-certificates.crt", // Linux etc
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL
"/etc/ssl/ca-bundle.pem", // OpenSUSE
}
func initDefaultRoots() {
roots := x509.NewCertPool()
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
break
}
}
varDefaultRoots = roots
}
// Copyright 2011 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 tls
import (
"crypto/x509"
"reflect"
"syscall"
"unsafe"
)
func loadStore(roots *x509.CertPool, name string) {
store, errno := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name))
if errno != 0 {
return
}
var cert *syscall.CertContext
for {
cert = syscall.CertEnumCertificatesInStore(store, cert)
if cert == nil {
break
}
var asn1Slice []byte
hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&asn1Slice))
hdrp.Data = cert.EncodedCert
hdrp.Len = int(cert.Length)
hdrp.Cap = int(cert.Length)
buf := make([]byte, len(asn1Slice))
copy(buf, asn1Slice)
if cert, err := x509.ParseCertificate(buf); err == nil {
roots.AddCert(cert)
}
}
syscall.CertCloseStore(store, 0)
}
func initDefaultRoots() {
roots := x509.NewCertPool()
// Roots
loadStore(roots, "ROOT")
// Intermediates
loadStore(roots, "CA")
varDefaultRoots = roots
}
...@@ -5,9 +5,7 @@ ...@@ -5,9 +5,7 @@
package x509 package x509
import ( import (
"crypto/x509/pkix"
"encoding/pem" "encoding/pem"
"strings"
) )
// Roots is a set of certificates. // Roots is a set of certificates.
...@@ -26,10 +24,6 @@ func NewCertPool() *CertPool { ...@@ -26,10 +24,6 @@ func NewCertPool() *CertPool {
} }
} }
func nameToKey(name *pkix.Name) string {
return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName
}
// findVerifiedParents attempts to find certificates in s which have signed the // findVerifiedParents attempts to find certificates in s which have signed the
// given certificate. If no such certificate can be found or the signature // given certificate. If no such certificate can be found or the signature
// doesn't match, it returns nil. // doesn't match, it returns nil.
...@@ -40,7 +34,7 @@ func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) { ...@@ -40,7 +34,7 @@ func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) {
candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)] candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
} }
if len(candidates) == 0 { if len(candidates) == 0 {
candidates = s.byName[nameToKey(&cert.Issuer)] candidates = s.byName[string(cert.RawIssuer)]
} }
for _, c := range candidates { for _, c := range candidates {
...@@ -72,7 +66,7 @@ func (s *CertPool) AddCert(cert *Certificate) { ...@@ -72,7 +66,7 @@ func (s *CertPool) AddCert(cert *Certificate) {
keyId := string(cert.SubjectKeyId) keyId := string(cert.SubjectKeyId)
s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n) s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n)
} }
name := nameToKey(&cert.Subject) name := string(cert.RawSubject)
s.byName[name] = append(s.byName[name], n) s.byName[name] = append(s.byName[name], n)
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment