libgo: Update to October 24 version of master library.

From-SVN: r204466
This commit is contained in:
Ian Lance Taylor 2013-11-06 19:49:01 +00:00
parent f20f261304
commit f038dae646
596 changed files with 32029 additions and 7466 deletions

View file

@ -1,4 +1,4 @@
a7bd9a33067b 7ebbddd21330
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.

View file

@ -37,7 +37,8 @@ AM_CPPFLAGS = -I $(srcdir)/runtime $(LIBFFIINCS) $(PTHREAD_CFLAGS)
ACLOCAL_AMFLAGS = -I ./config -I ../config ACLOCAL_AMFLAGS = -I ./config -I ../config
AM_CFLAGS = -fexceptions -fplan9-extensions $(SPLIT_STACK) $(WARN_CFLAGS) \ AM_CFLAGS = -fexceptions -fnon-call-exceptions -fplan9-extensions \
$(SPLIT_STACK) $(WARN_CFLAGS) \
$(STRINGOPS_FLAG) $(OSCFLAGS) \ $(STRINGOPS_FLAG) $(OSCFLAGS) \
-I $(srcdir)/../libgcc -I $(srcdir)/../libbacktrace \ -I $(srcdir)/../libgcc -I $(srcdir)/../libbacktrace \
-I $(MULTIBUILDTOP)../../gcc/include -I $(MULTIBUILDTOP)../../gcc/include
@ -103,6 +104,7 @@ toolexeclibgo_DATA = \
bufio.gox \ bufio.gox \
bytes.gox \ bytes.gox \
crypto.gox \ crypto.gox \
encoding.gox \
errors.gox \ errors.gox \
expvar.gox \ expvar.gox \
flag.gox \ flag.gox \
@ -251,6 +253,11 @@ toolexeclibgoimage_DATA = \
image/jpeg.gox \ image/jpeg.gox \
image/png.gox image/png.gox
toolexeclibgoimagecolordir = $(toolexeclibgoimagedir)/color
toolexeclibgoimagecolor_DATA = \
image/color/palette.gox
toolexeclibgoindexdir = $(toolexeclibgodir)/index toolexeclibgoindexdir = $(toolexeclibgodir)/index
toolexeclibgoindex_DATA = \ toolexeclibgoindex_DATA = \
@ -573,6 +580,9 @@ go_bytes_c_files = \
go_crypto_files = \ go_crypto_files = \
go/crypto/crypto.go go/crypto/crypto.go
go_encoding_files = \
go/encoding/encoding.go
go_errors_files = \ go_errors_files = \
go/errors/errors.go go/errors/errors.go
@ -669,7 +679,7 @@ go_net_fd_os_file =
go_net_newpollserver_file = go_net_newpollserver_file =
else # !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS else # !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS
if LIBGO_IS_NETBSD if LIBGO_IS_NETBSD
go_net_fd_os_file = go/net/fd_bsd.go go_net_fd_os_file =
go_net_newpollserver_file = go_net_newpollserver_file =
else # !LIBGO_IS_NETBSD && !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS else # !LIBGO_IS_NETBSD && !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS
# By default use select with pipes. Most systems should have # By default use select with pipes. Most systems should have
@ -726,9 +736,13 @@ else
if LIBGO_IS_FREEBSD if LIBGO_IS_FREEBSD
go_net_sendfile_file = go/net/sendfile_freebsd.go go_net_sendfile_file = go/net/sendfile_freebsd.go
else else
if LIBGO_IS_DRAGONFLY
go_net_sendfile_file = go/net/sendfile_dragonfly.go
else
go_net_sendfile_file = go/net/sendfile_stub.go go_net_sendfile_file = go/net/sendfile_stub.go
endif endif
endif endif
endif
if LIBGO_IS_LINUX if LIBGO_IS_LINUX
go_net_interface_file = go/net/interface_linux.go go_net_interface_file = go/net/interface_linux.go
@ -736,9 +750,13 @@ else
if LIBGO_IS_NETBSD if LIBGO_IS_NETBSD
go_net_interface_file = go/net/interface_netbsd.go go_net_interface_file = go/net/interface_netbsd.go
else else
if LIBGO_IS_DRAGONFLY
go_net_interface_file = go/net/interface_dragonfly.go
else
go_net_interface_file = go/net/interface_stub.go go_net_interface_file = go/net/interface_stub.go
endif endif
endif endif
endif
if LIBGO_IS_LINUX if LIBGO_IS_LINUX
go_net_cloexec_file = go/net/sock_cloexec.go go_net_cloexec_file = go/net/sock_cloexec.go
@ -746,13 +764,13 @@ else
go_net_cloexec_file = go/net/sys_cloexec.go go_net_cloexec_file = go/net/sys_cloexec.go
endif endif
if LIBGO_IS_LINUX if LIBGO_IS_OPENBSD
go_net_poll_file = go/net/fd_poll_runtime.go go_net_tcpsockopt_file = go/net/tcpsockopt_openbsd.go
else else
if LIBGO_IS_DARWIN if LIBGO_IS_DARWIN
go_net_poll_file = go/net/fd_poll_runtime.go go_net_tcpsockopt_file = go/net/tcpsockopt_darwin.go
else else
go_net_poll_file = go/net/fd_poll_unix.go go_net_tcpsockopt_file = go/net/tcpsockopt_unix.go
endif endif
endif endif
@ -766,6 +784,7 @@ go_net_files = \
go/net/dnsconfig_unix.go \ go/net/dnsconfig_unix.go \
go/net/dnsmsg.go \ go/net/dnsmsg.go \
$(go_net_newpollserver_file) \ $(go_net_newpollserver_file) \
go/net/fd_mutex.go \
go/net/fd_unix.go \ go/net/fd_unix.go \
$(go_net_fd_os_file) \ $(go_net_fd_os_file) \
go/net/file_unix.go \ go/net/file_unix.go \
@ -783,18 +802,21 @@ go_net_files = \
go/net/net.go \ go/net/net.go \
go/net/parse.go \ go/net/parse.go \
go/net/pipe.go \ go/net/pipe.go \
$(go_net_poll_file) \ go/net/fd_poll_runtime.go \
go/net/port.go \ go/net/port.go \
go/net/port_unix.go \ go/net/port_unix.go \
go/net/race0.go \
$(go_net_sendfile_file) \ $(go_net_sendfile_file) \
go/net/singleflight.go \
go/net/sock_posix.go \ go/net/sock_posix.go \
go/net/sock_unix.go \
$(go_net_sock_file) \ $(go_net_sock_file) \
go/net/sockopt_posix.go \ go/net/sockopt_posix.go \
$(go_net_sockopt_file) \ $(go_net_sockopt_file) \
$(go_net_sockoptip_file) \ $(go_net_sockoptip_file) \
go/net/tcpsock.go \ go/net/tcpsock.go \
go/net/tcpsock_posix.go \ go/net/tcpsock_posix.go \
go/net/tcpsockopt_posix.go \
$(go_net_tcpsockopt_file) \
go/net/udpsock.go \ go/net/udpsock.go \
go/net/udpsock_posix.go \ go/net/udpsock_posix.go \
go/net/unixsock.go \ go/net/unixsock.go \
@ -818,6 +840,12 @@ go_os_dir_file = go/os/dir_regfile.go
endif endif
endif endif
if LIBGO_IS_DARWIN
go_os_getwd_file = go/os/getwd_darwin.go
else
go_os_getwd_file =
endif
if LIBGO_IS_LINUX if LIBGO_IS_LINUX
go_os_sys_file = go/os/sys_linux.go go_os_sys_file = go/os/sys_linux.go
else else
@ -854,6 +882,9 @@ else
if LIBGO_IS_NETBSD if LIBGO_IS_NETBSD
go_os_stat_file = go/os/stat_atimespec.go go_os_stat_file = go/os/stat_atimespec.go
else else
if LIBGO_IS_DRAGONFLY
go_os_stat_file = go/os/stat_dragonfly.go
else
go_os_stat_file = go/os/stat.go go_os_stat_file = go/os/stat.go
endif endif
endif endif
@ -861,6 +892,7 @@ endif
endif endif
endif endif
endif endif
endif
if LIBGO_IS_LINUX if LIBGO_IS_LINUX
go_os_pipe_file = go/os/pipe_linux.go go_os_pipe_file = go/os/pipe_linux.go
@ -874,7 +906,7 @@ go_os_files = \
go/os/doc.go \ go/os/doc.go \
go/os/env.go \ go/os/env.go \
go/os/error.go \ go/os/error.go \
go/os/error_posix.go \ go/os/error_unix.go \
go/os/exec.go \ go/os/exec.go \
go/os/exec_posix.go \ go/os/exec_posix.go \
go/os/exec_unix.go \ go/os/exec_unix.go \
@ -882,6 +914,7 @@ go_os_files = \
go/os/file_posix.go \ go/os/file_posix.go \
go/os/file_unix.go \ go/os/file_unix.go \
go/os/getwd.go \ go/os/getwd.go \
$(go_os_getwd_file) \
go/os/path.go \ go/os/path.go \
go/os/path_unix.go \ go/os/path_unix.go \
$(go_os_pipe_file) \ $(go_os_pipe_file) \
@ -970,7 +1003,10 @@ go_strings_files = \
go/strings/reader.go \ go/strings/reader.go \
go/strings/replace.go \ go/strings/replace.go \
go/strings/search.go \ go/strings/search.go \
go/strings/strings.go go/strings/strings.go \
go/strings/strings_decl.go
go_strings_c_files = \
go/strings/indexbyte.c
go_sync_files = \ go_sync_files = \
go/sync/cond.go \ go/sync/cond.go \
@ -1000,6 +1036,7 @@ go_syslog_c_files = \
go_testing_files = \ go_testing_files = \
go/testing/allocs.go \ go/testing/allocs.go \
go/testing/benchmark.go \ go/testing/benchmark.go \
go/testing/cover.go \
go/testing/example.go \ go/testing/example.go \
go/testing/testing.go go/testing/testing.go
@ -1048,6 +1085,7 @@ go_archive_tar_files = \
go_archive_zip_files = \ go_archive_zip_files = \
go/archive/zip/reader.go \ go/archive/zip/reader.go \
go/archive/zip/register.go \
go/archive/zip/struct.go \ go/archive/zip/struct.go \
go/archive/zip/writer.go go/archive/zip/writer.go
@ -1098,6 +1136,7 @@ go_crypto_cipher_files = \
go/crypto/cipher/cfb.go \ go/crypto/cipher/cfb.go \
go/crypto/cipher/cipher.go \ go/crypto/cipher/cipher.go \
go/crypto/cipher/ctr.go \ go/crypto/cipher/ctr.go \
go/crypto/cipher/gcm.go \
go/crypto/cipher/io.go \ go/crypto/cipher/io.go \
go/crypto/cipher/ofb.go go/crypto/cipher/ofb.go
go_crypto_des_files = \ go_crypto_des_files = \
@ -1110,7 +1149,8 @@ go_crypto_ecdsa_files = \
go/crypto/ecdsa/ecdsa.go go/crypto/ecdsa/ecdsa.go
go_crypto_elliptic_files = \ go_crypto_elliptic_files = \
go/crypto/elliptic/elliptic.go \ go/crypto/elliptic/elliptic.go \
go/crypto/elliptic/p224.go go/crypto/elliptic/p224.go \
go/crypto/elliptic/p256.go
go_crypto_hmac_files = \ go_crypto_hmac_files = \
go/crypto/hmac/hmac.go go/crypto/hmac/hmac.go
go_crypto_md5_files = \ go_crypto_md5_files = \
@ -1125,6 +1165,7 @@ go_crypto_rc4_files = \
go/crypto/rc4/rc4_ref.go go/crypto/rc4/rc4_ref.go
go_crypto_rsa_files = \ go_crypto_rsa_files = \
go/crypto/rsa/pkcs1v15.go \ go/crypto/rsa/pkcs1v15.go \
go/crypto/rsa/pss.go \
go/crypto/rsa/rsa.go go/crypto/rsa/rsa.go
go_crypto_sha1_files = \ go_crypto_sha1_files = \
go/crypto/sha1/sha1.go \ go/crypto/sha1/sha1.go \
@ -1308,11 +1349,15 @@ go_image_color_files = \
go/image/color/color.go \ go/image/color/color.go \
go/image/color/ycbcr.go go/image/color/ycbcr.go
go_image_color_palette_files = \
go/image/color/palette/palette.go
go_image_draw_files = \ go_image_draw_files = \
go/image/draw/draw.go go/image/draw/draw.go
go_image_gif_files = \ go_image_gif_files = \
go/image/gif/reader.go go/image/gif/reader.go \
go/image/gif/writer.go
go_image_jpeg_files = \ go_image_jpeg_files = \
go/image/jpeg/fdct.go \ go/image/jpeg/fdct.go \
@ -1766,6 +1811,7 @@ libgo_go_objs = \
bytes.lo \ bytes.lo \
bytes/index.lo \ bytes/index.lo \
crypto.lo \ crypto.lo \
encoding.lo \
errors.lo \ errors.lo \
expvar.lo \ expvar.lo \
flag.lo \ flag.lo \
@ -1787,6 +1833,7 @@ libgo_go_objs = \
sort.lo \ sort.lo \
strconv.lo \ strconv.lo \
strings.lo \ strings.lo \
strings/index.lo \
sync.lo \ sync.lo \
syscall.lo \ syscall.lo \
syscall/errno.lo \ syscall/errno.lo \
@ -1863,6 +1910,7 @@ libgo_go_objs = \
net/http/httputil.lo \ net/http/httputil.lo \
net/http/pprof.lo \ net/http/pprof.lo \
image/color.lo \ image/color.lo \
image/color/palette.lo \
image/draw.lo \ image/draw.lo \
image/gif.lo \ image/gif.lo \
image/jpeg.lo \ image/jpeg.lo \
@ -2033,6 +2081,15 @@ crypto/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: crypto/check .PHONY: crypto/check
@go_include@ encoding.lo.dep
encoding.lo.dep: $(go_encoding_files)
$(BUILDDEPS)
encoding.lo: $(go_encoding_files)
$(BUILDPACKAGE)
encoding/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: encoding/check
@go_include@ errors.lo.dep @go_include@ errors.lo.dep
errors.lo.dep: $(go_errors_files) errors.lo.dep: $(go_errors_files)
$(BUILDDEPS) $(BUILDDEPS)
@ -2214,6 +2271,9 @@ strings.lo.dep: $(go_strings_files)
$(BUILDDEPS) $(BUILDDEPS)
strings.lo: $(go_strings_files) strings.lo: $(go_strings_files)
$(BUILDPACKAGE) $(BUILDPACKAGE)
strings/index.lo: $(go_strings_c_files)
@$(MKDIR_P) strings
$(LTCOMPILE) -c -o strings/index.lo $(srcdir)/go/strings/indexbyte.c
strings/check: $(CHECK_DEPS) strings/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: strings/check .PHONY: strings/check
@ -2821,6 +2881,15 @@ image/color/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: image/color/check .PHONY: image/color/check
@go_include@ image/color/palette.lo.dep
image/color/palette.lo.dep: $(go_image_color_palette_files)
$(BUILDDEPS)
image/color/palette.lo: $(go_image_color_palette_files)
$(BUILDPACKAGE)
image/color/palette/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: image/color/palette/check
@go_include@ image/draw.lo.dep @go_include@ image/draw.lo.dep
image/draw.lo.dep: $(go_image_draw_files) image/draw.lo.dep: $(go_image_draw_files)
$(BUILDDEPS) $(BUILDDEPS)
@ -3236,6 +3305,8 @@ bytes.gox: bytes.lo
$(BUILDGOX) $(BUILDGOX)
crypto.gox: crypto.lo crypto.gox: crypto.lo
$(BUILDGOX) $(BUILDGOX)
encoding.gox: encoding.lo
$(BUILDGOX)
errors.gox: errors.lo errors.gox: errors.lo
$(BUILDGOX) $(BUILDGOX)
expvar.gox: expvar.lo expvar.gox: expvar.lo
@ -3433,6 +3504,9 @@ image/jpeg.gox: image/jpeg.lo
image/png.gox: image/png.lo image/png.gox: image/png.lo
$(BUILDGOX) $(BUILDGOX)
image/color/palette.gox: image/color/palette.lo
$(BUILDGOX)
index/suffixarray.gox: index/suffixarray.lo index/suffixarray.gox: index/suffixarray.lo
$(BUILDGOX) $(BUILDGOX)

View file

@ -105,6 +105,7 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \
"$(DESTDIR)$(toolexeclibgohashdir)" \ "$(DESTDIR)$(toolexeclibgohashdir)" \
"$(DESTDIR)$(toolexeclibgohtmldir)" \ "$(DESTDIR)$(toolexeclibgohtmldir)" \
"$(DESTDIR)$(toolexeclibgoimagedir)" \ "$(DESTDIR)$(toolexeclibgoimagedir)" \
"$(DESTDIR)$(toolexeclibgoimagecolordir)" \
"$(DESTDIR)$(toolexeclibgoindexdir)" \ "$(DESTDIR)$(toolexeclibgoindexdir)" \
"$(DESTDIR)$(toolexeclibgoiodir)" \ "$(DESTDIR)$(toolexeclibgoiodir)" \
"$(DESTDIR)$(toolexeclibgologdir)" \ "$(DESTDIR)$(toolexeclibgologdir)" \
@ -132,12 +133,12 @@ libgobegin_a_OBJECTS = $(am_libgobegin_a_OBJECTS)
LTLIBRARIES = $(toolexeclib_LTLIBRARIES) LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
am__DEPENDENCIES_1 = am__DEPENDENCIES_1 =
am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \
errors.lo expvar.lo flag.lo fmt.lo hash.lo html.lo image.lo \ encoding.lo errors.lo expvar.lo flag.lo fmt.lo hash.lo html.lo \
io.lo log.lo math.lo mime.lo net.lo os.lo path.lo \ image.lo io.lo log.lo math.lo mime.lo net.lo os.lo path.lo \
reflect-go.lo reflect/makefunc.lo regexp.lo runtime-go.lo \ reflect-go.lo reflect/makefunc.lo regexp.lo runtime-go.lo \
sort.lo strconv.lo strings.lo sync.lo syscall.lo \ sort.lo strconv.lo strings.lo strings/index.lo sync.lo \
syscall/errno.lo syscall/signame.lo syscall/wait.lo testing.lo \ syscall.lo syscall/errno.lo syscall/signame.lo syscall/wait.lo \
time-go.lo unicode.lo archive/tar.lo archive/zip.lo \ testing.lo time-go.lo unicode.lo archive/tar.lo archive/zip.lo \
compress/bzip2.lo compress/flate.lo compress/gzip.lo \ compress/bzip2.lo compress/flate.lo compress/gzip.lo \
compress/lzw.lo compress/zlib.lo container/heap.lo \ compress/lzw.lo compress/zlib.lo container/heap.lo \
container/list.lo container/ring.lo crypto/aes.lo \ container/list.lo container/ring.lo crypto/aes.lo \
@ -157,13 +158,13 @@ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \
hash/crc64.lo hash/fnv.lo net/http/cgi.lo \ hash/crc64.lo hash/fnv.lo net/http/cgi.lo \
net/http/cookiejar.lo net/http/fcgi.lo net/http/httptest.lo \ net/http/cookiejar.lo net/http/fcgi.lo net/http/httptest.lo \
net/http/httputil.lo net/http/pprof.lo image/color.lo \ net/http/httputil.lo net/http/pprof.lo image/color.lo \
image/draw.lo image/gif.lo image/jpeg.lo image/png.lo \ image/color/palette.lo image/draw.lo image/gif.lo \
index/suffixarray.lo io/ioutil.lo log/syslog.lo \ image/jpeg.lo image/png.lo index/suffixarray.lo io/ioutil.lo \
log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \ log/syslog.lo log/syslog/syslog_c.lo math/big.lo math/cmplx.lo \
mime/multipart.lo net/http.lo net/mail.lo net/rpc.lo \ math/rand.lo mime/multipart.lo net/http.lo net/mail.lo \
net/smtp.lo net/textproto.lo net/url.lo old/regexp.lo \ net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \
old/template.lo os/exec.lo $(am__DEPENDENCIES_1) os/signal.lo \ old/regexp.lo old/template.lo os/exec.lo $(am__DEPENDENCIES_1) \
os/user.lo path/filepath.lo regexp/syntax.lo \ os/signal.lo os/user.lo path/filepath.lo regexp/syntax.lo \
net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \ net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \
sync/atomic.lo sync/atomic_c.lo text/scanner.lo \ sync/atomic.lo sync/atomic_c.lo text/scanner.lo \
text/tabwriter.lo text/template.lo text/template/parse.lo \ text/tabwriter.lo text/template.lo text/template/parse.lo \
@ -260,16 +261,16 @@ DATA = $(toolexeclibgo_DATA) $(toolexeclibgoarchive_DATA) \
$(toolexeclibgodebug_DATA) $(toolexeclibgoencoding_DATA) \ $(toolexeclibgodebug_DATA) $(toolexeclibgoencoding_DATA) \
$(toolexeclibgoexp_DATA) $(toolexeclibgogo_DATA) \ $(toolexeclibgoexp_DATA) $(toolexeclibgogo_DATA) \
$(toolexeclibgohash_DATA) $(toolexeclibgohtml_DATA) \ $(toolexeclibgohash_DATA) $(toolexeclibgohtml_DATA) \
$(toolexeclibgoimage_DATA) $(toolexeclibgoindex_DATA) \ $(toolexeclibgoimage_DATA) $(toolexeclibgoimagecolor_DATA) \
$(toolexeclibgoio_DATA) $(toolexeclibgolog_DATA) \ $(toolexeclibgoindex_DATA) $(toolexeclibgoio_DATA) \
$(toolexeclibgomath_DATA) $(toolexeclibgomime_DATA) \ $(toolexeclibgolog_DATA) $(toolexeclibgomath_DATA) \
$(toolexeclibgonet_DATA) $(toolexeclibgonethttp_DATA) \ $(toolexeclibgomime_DATA) $(toolexeclibgonet_DATA) \
$(toolexeclibgonetrpc_DATA) $(toolexeclibgoold_DATA) \ $(toolexeclibgonethttp_DATA) $(toolexeclibgonetrpc_DATA) \
$(toolexeclibgoos_DATA) $(toolexeclibgopath_DATA) \ $(toolexeclibgoold_DATA) $(toolexeclibgoos_DATA) \
$(toolexeclibgoregexp_DATA) $(toolexeclibgoruntime_DATA) \ $(toolexeclibgopath_DATA) $(toolexeclibgoregexp_DATA) \
$(toolexeclibgosync_DATA) $(toolexeclibgotesting_DATA) \ $(toolexeclibgoruntime_DATA) $(toolexeclibgosync_DATA) \
$(toolexeclibgotext_DATA) $(toolexeclibgotexttemplate_DATA) \ $(toolexeclibgotesting_DATA) $(toolexeclibgotext_DATA) \
$(toolexeclibgounicode_DATA) $(toolexeclibgotexttemplate_DATA) $(toolexeclibgounicode_DATA)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive distclean-recursive maintainer-clean-recursive
AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
@ -443,7 +444,8 @@ WARN_CFLAGS = $(WARN_FLAGS) $(WERROR)
# -I/-D flags to pass when compiling. # -I/-D flags to pass when compiling.
AM_CPPFLAGS = -I $(srcdir)/runtime $(LIBFFIINCS) $(PTHREAD_CFLAGS) AM_CPPFLAGS = -I $(srcdir)/runtime $(LIBFFIINCS) $(PTHREAD_CFLAGS)
ACLOCAL_AMFLAGS = -I ./config -I ../config ACLOCAL_AMFLAGS = -I ./config -I ../config
AM_CFLAGS = -fexceptions -fplan9-extensions $(SPLIT_STACK) $(WARN_CFLAGS) \ AM_CFLAGS = -fexceptions -fnon-call-exceptions -fplan9-extensions \
$(SPLIT_STACK) $(WARN_CFLAGS) \
$(STRINGOPS_FLAG) $(OSCFLAGS) \ $(STRINGOPS_FLAG) $(OSCFLAGS) \
-I $(srcdir)/../libgcc -I $(srcdir)/../libbacktrace \ -I $(srcdir)/../libgcc -I $(srcdir)/../libbacktrace \
-I $(MULTIBUILDTOP)../../gcc/include -I $(MULTIBUILDTOP)../../gcc/include
@ -506,6 +508,7 @@ toolexeclibgo_DATA = \
bufio.gox \ bufio.gox \
bytes.gox \ bytes.gox \
crypto.gox \ crypto.gox \
encoding.gox \
errors.gox \ errors.gox \
expvar.gox \ expvar.gox \
flag.gox \ flag.gox \
@ -640,6 +643,10 @@ toolexeclibgoimage_DATA = \
image/jpeg.gox \ image/jpeg.gox \
image/png.gox image/png.gox
toolexeclibgoimagecolordir = $(toolexeclibgoimagedir)/color
toolexeclibgoimagecolor_DATA = \
image/color/palette.gox
toolexeclibgoindexdir = $(toolexeclibgodir)/index toolexeclibgoindexdir = $(toolexeclibgodir)/index
toolexeclibgoindex_DATA = \ toolexeclibgoindex_DATA = \
index/suffixarray.gox index/suffixarray.gox
@ -865,6 +872,9 @@ go_bytes_c_files = \
go_crypto_files = \ go_crypto_files = \
go/crypto/crypto.go go/crypto/crypto.go
go_encoding_files = \
go/encoding/encoding.go
go_errors_files = \ go_errors_files = \
go/errors/errors.go go/errors/errors.go
@ -955,7 +965,7 @@ go_mime_files = \
# By default use select with pipes. Most systems should have # By default use select with pipes. Most systems should have
# something better. # something better.
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_select.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_select.go
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_bsd.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file =
@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file =
@LIBGO_IS_RTEMS_TRUE@go_net_fd_os_file = go/net/fd_select.go @LIBGO_IS_RTEMS_TRUE@go_net_fd_os_file = go/net/fd_select.go
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file =
@ -986,17 +996,19 @@ go_mime_files = \
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
@LIBGO_IS_LINUX_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go @LIBGO_IS_LINUX_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go @LIBGO_IS_DRAGONFLY_FALSE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go
@LIBGO_IS_DRAGONFLY_TRUE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_dragonfly.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_freebsd.go @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_freebsd.go
@LIBGO_IS_LINUX_TRUE@go_net_sendfile_file = go/net/sendfile_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sendfile_file = go/net/sendfile_linux.go
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@go_net_interface_file = go/net/interface_stub.go @LIBGO_IS_DRAGONFLY_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@go_net_interface_file = go/net/interface_stub.go
@LIBGO_IS_DRAGONFLY_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@go_net_interface_file = go/net/interface_dragonfly.go
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@go_net_interface_file = go/net/interface_netbsd.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@go_net_interface_file = go/net/interface_netbsd.go
@LIBGO_IS_LINUX_TRUE@go_net_interface_file = go/net/interface_linux.go @LIBGO_IS_LINUX_TRUE@go_net_interface_file = go/net/interface_linux.go
@LIBGO_IS_LINUX_FALSE@go_net_cloexec_file = go/net/sys_cloexec.go @LIBGO_IS_LINUX_FALSE@go_net_cloexec_file = go/net/sys_cloexec.go
@LIBGO_IS_LINUX_TRUE@go_net_cloexec_file = go/net/sock_cloexec.go @LIBGO_IS_LINUX_TRUE@go_net_cloexec_file = go/net/sock_cloexec.go
@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_poll_file = go/net/fd_poll_unix.go @LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_OPENBSD_FALSE@go_net_tcpsockopt_file = go/net/tcpsockopt_unix.go
@LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_poll_file = go/net/fd_poll_runtime.go @LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_OPENBSD_FALSE@go_net_tcpsockopt_file = go/net/tcpsockopt_darwin.go
@LIBGO_IS_LINUX_TRUE@go_net_poll_file = go/net/fd_poll_runtime.go @LIBGO_IS_OPENBSD_TRUE@go_net_tcpsockopt_file = go/net/tcpsockopt_openbsd.go
go_net_files = \ go_net_files = \
go/net/cgo_unix.go \ go/net/cgo_unix.go \
$(go_net_cgo_file) \ $(go_net_cgo_file) \
@ -1007,6 +1019,7 @@ go_net_files = \
go/net/dnsconfig_unix.go \ go/net/dnsconfig_unix.go \
go/net/dnsmsg.go \ go/net/dnsmsg.go \
$(go_net_newpollserver_file) \ $(go_net_newpollserver_file) \
go/net/fd_mutex.go \
go/net/fd_unix.go \ go/net/fd_unix.go \
$(go_net_fd_os_file) \ $(go_net_fd_os_file) \
go/net/file_unix.go \ go/net/file_unix.go \
@ -1024,18 +1037,21 @@ go_net_files = \
go/net/net.go \ go/net/net.go \
go/net/parse.go \ go/net/parse.go \
go/net/pipe.go \ go/net/pipe.go \
$(go_net_poll_file) \ go/net/fd_poll_runtime.go \
go/net/port.go \ go/net/port.go \
go/net/port_unix.go \ go/net/port_unix.go \
go/net/race0.go \
$(go_net_sendfile_file) \ $(go_net_sendfile_file) \
go/net/singleflight.go \
go/net/sock_posix.go \ go/net/sock_posix.go \
go/net/sock_unix.go \
$(go_net_sock_file) \ $(go_net_sock_file) \
go/net/sockopt_posix.go \ go/net/sockopt_posix.go \
$(go_net_sockopt_file) \ $(go_net_sockopt_file) \
$(go_net_sockoptip_file) \ $(go_net_sockoptip_file) \
go/net/tcpsock.go \ go/net/tcpsock.go \
go/net/tcpsock_posix.go \ go/net/tcpsock_posix.go \
go/net/tcpsockopt_posix.go \
$(go_net_tcpsockopt_file) \
go/net/udpsock.go \ go/net/udpsock.go \
go/net/udpsock_posix.go \ go/net/udpsock_posix.go \
go/net/unixsock.go \ go/net/unixsock.go \
@ -1046,12 +1062,15 @@ go_net_files = \
@LIBGO_IS_386_TRUE@@LIBGO_IS_SOLARIS_TRUE@go_os_dir_file = go/os/dir_largefile.go @LIBGO_IS_386_TRUE@@LIBGO_IS_SOLARIS_TRUE@go_os_dir_file = go/os/dir_largefile.go
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_dir_file = go/os/dir_regfile.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_dir_file = go/os/dir_regfile.go
@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_os_dir_file = go/os/dir_largefile.go @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_os_dir_file = go/os/dir_largefile.go
@LIBGO_IS_DARWIN_FALSE@go_os_getwd_file =
@LIBGO_IS_DARWIN_TRUE@go_os_getwd_file = go/os/getwd_darwin.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_sys_file = go/os/sys_bsd.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_sys_file = go/os/sys_bsd.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_os_sys_file = go/os/sys_uname.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_os_sys_file = go/os/sys_uname.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_sys_file = go/os/sys_uname.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_sys_file = go/os/sys_uname.go
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_os_sys_file = go/os/sys_uname.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_os_sys_file = go/os/sys_uname.go
@LIBGO_IS_LINUX_TRUE@go_os_sys_file = go/os/sys_linux.go @LIBGO_IS_LINUX_TRUE@go_os_sys_file = go/os/sys_linux.go
@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat.go @LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_DRAGONFLY_FALSE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat.go
@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_DRAGONFLY_TRUE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_dragonfly.go
@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_atimespec.go @LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_atimespec.go
@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_atimespec.go @LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_atimespec.go
@LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_atimespec.go @LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_atimespec.go
@ -1066,7 +1085,7 @@ go_os_files = \
go/os/doc.go \ go/os/doc.go \
go/os/env.go \ go/os/env.go \
go/os/error.go \ go/os/error.go \
go/os/error_posix.go \ go/os/error_unix.go \
go/os/exec.go \ go/os/exec.go \
go/os/exec_posix.go \ go/os/exec_posix.go \
go/os/exec_unix.go \ go/os/exec_unix.go \
@ -1074,6 +1093,7 @@ go_os_files = \
go/os/file_posix.go \ go/os/file_posix.go \
go/os/file_unix.go \ go/os/file_unix.go \
go/os/getwd.go \ go/os/getwd.go \
$(go_os_getwd_file) \
go/os/path.go \ go/os/path.go \
go/os/path_unix.go \ go/os/path_unix.go \
$(go_os_pipe_file) \ $(go_os_pipe_file) \
@ -1149,7 +1169,11 @@ go_strings_files = \
go/strings/reader.go \ go/strings/reader.go \
go/strings/replace.go \ go/strings/replace.go \
go/strings/search.go \ go/strings/search.go \
go/strings/strings.go go/strings/strings.go \
go/strings/strings_decl.go
go_strings_c_files = \
go/strings/indexbyte.c
go_sync_files = \ go_sync_files = \
go/sync/cond.go \ go/sync/cond.go \
@ -1173,6 +1197,7 @@ go_syslog_c_files = \
go_testing_files = \ go_testing_files = \
go/testing/allocs.go \ go/testing/allocs.go \
go/testing/benchmark.go \ go/testing/benchmark.go \
go/testing/cover.go \
go/testing/example.go \ go/testing/example.go \
go/testing/testing.go go/testing/testing.go
@ -1208,6 +1233,7 @@ go_archive_tar_files = \
go_archive_zip_files = \ go_archive_zip_files = \
go/archive/zip/reader.go \ go/archive/zip/reader.go \
go/archive/zip/register.go \
go/archive/zip/struct.go \ go/archive/zip/struct.go \
go/archive/zip/writer.go go/archive/zip/writer.go
@ -1259,6 +1285,7 @@ go_crypto_cipher_files = \
go/crypto/cipher/cfb.go \ go/crypto/cipher/cfb.go \
go/crypto/cipher/cipher.go \ go/crypto/cipher/cipher.go \
go/crypto/cipher/ctr.go \ go/crypto/cipher/ctr.go \
go/crypto/cipher/gcm.go \
go/crypto/cipher/io.go \ go/crypto/cipher/io.go \
go/crypto/cipher/ofb.go go/crypto/cipher/ofb.go
@ -1275,7 +1302,8 @@ go_crypto_ecdsa_files = \
go_crypto_elliptic_files = \ go_crypto_elliptic_files = \
go/crypto/elliptic/elliptic.go \ go/crypto/elliptic/elliptic.go \
go/crypto/elliptic/p224.go go/crypto/elliptic/p224.go \
go/crypto/elliptic/p256.go
go_crypto_hmac_files = \ go_crypto_hmac_files = \
go/crypto/hmac/hmac.go go/crypto/hmac/hmac.go
@ -1295,6 +1323,7 @@ go_crypto_rc4_files = \
go_crypto_rsa_files = \ go_crypto_rsa_files = \
go/crypto/rsa/pkcs1v15.go \ go/crypto/rsa/pkcs1v15.go \
go/crypto/rsa/pss.go \
go/crypto/rsa/rsa.go go/crypto/rsa/rsa.go
go_crypto_sha1_files = \ go_crypto_sha1_files = \
@ -1509,11 +1538,15 @@ go_image_color_files = \
go/image/color/color.go \ go/image/color/color.go \
go/image/color/ycbcr.go go/image/color/ycbcr.go
go_image_color_palette_files = \
go/image/color/palette/palette.go
go_image_draw_files = \ go_image_draw_files = \
go/image/draw/draw.go go/image/draw/draw.go
go_image_gif_files = \ go_image_gif_files = \
go/image/gif/reader.go go/image/gif/reader.go \
go/image/gif/writer.go
go_image_jpeg_files = \ go_image_jpeg_files = \
go/image/jpeg/fdct.go \ go/image/jpeg/fdct.go \
@ -1849,6 +1882,7 @@ libgo_go_objs = \
bytes.lo \ bytes.lo \
bytes/index.lo \ bytes/index.lo \
crypto.lo \ crypto.lo \
encoding.lo \
errors.lo \ errors.lo \
expvar.lo \ expvar.lo \
flag.lo \ flag.lo \
@ -1870,6 +1904,7 @@ libgo_go_objs = \
sort.lo \ sort.lo \
strconv.lo \ strconv.lo \
strings.lo \ strings.lo \
strings/index.lo \
sync.lo \ sync.lo \
syscall.lo \ syscall.lo \
syscall/errno.lo \ syscall/errno.lo \
@ -1946,6 +1981,7 @@ libgo_go_objs = \
net/http/httputil.lo \ net/http/httputil.lo \
net/http/pprof.lo \ net/http/pprof.lo \
image/color.lo \ image/color.lo \
image/color/palette.lo \
image/draw.lo \ image/draw.lo \
image/gif.lo \ image/gif.lo \
image/jpeg.lo \ image/jpeg.lo \
@ -3518,6 +3554,26 @@ uninstall-toolexeclibgoimageDATA:
test -n "$$files" || exit 0; \ test -n "$$files" || exit 0; \
echo " ( cd '$(DESTDIR)$(toolexeclibgoimagedir)' && rm -f" $$files ")"; \ echo " ( cd '$(DESTDIR)$(toolexeclibgoimagedir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(toolexeclibgoimagedir)" && rm -f $$files cd "$(DESTDIR)$(toolexeclibgoimagedir)" && rm -f $$files
install-toolexeclibgoimagecolorDATA: $(toolexeclibgoimagecolor_DATA)
@$(NORMAL_INSTALL)
test -z "$(toolexeclibgoimagecolordir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoimagecolordir)"
@list='$(toolexeclibgoimagecolor_DATA)'; test -n "$(toolexeclibgoimagecolordir)" || list=; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
done | $(am__base_list) | \
while read files; do \
echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(toolexeclibgoimagecolordir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(toolexeclibgoimagecolordir)" || exit $$?; \
done
uninstall-toolexeclibgoimagecolorDATA:
@$(NORMAL_UNINSTALL)
@list='$(toolexeclibgoimagecolor_DATA)'; test -n "$(toolexeclibgoimagecolordir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
test -n "$$files" || exit 0; \
echo " ( cd '$(DESTDIR)$(toolexeclibgoimagecolordir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(toolexeclibgoimagecolordir)" && rm -f $$files
install-toolexeclibgoindexDATA: $(toolexeclibgoindex_DATA) install-toolexeclibgoindexDATA: $(toolexeclibgoindex_DATA)
@$(NORMAL_INSTALL) @$(NORMAL_INSTALL)
test -z "$(toolexeclibgoindexdir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoindexdir)" test -z "$(toolexeclibgoindexdir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoindexdir)"
@ -4019,7 +4075,7 @@ all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) all-multi $(DATA) \
config.h config.h
installdirs: installdirs-recursive installdirs: installdirs-recursive
installdirs-am: installdirs-am:
for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodatabasedir)" "$(DESTDIR)$(toolexeclibgodatabasesqldir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \ for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodatabasedir)" "$(DESTDIR)$(toolexeclibgodatabasesqldir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoimagecolordir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done done
install: install-recursive install: install-recursive
@ -4092,6 +4148,7 @@ install-exec-am: install-multi install-toolexeclibLIBRARIES \
install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \ install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
install-toolexeclibgogoDATA install-toolexeclibgohashDATA \ install-toolexeclibgogoDATA install-toolexeclibgohashDATA \
install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \ install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \
install-toolexeclibgoimagecolorDATA \
install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \ install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \
install-toolexeclibgologDATA install-toolexeclibgomathDATA \ install-toolexeclibgologDATA install-toolexeclibgomathDATA \
install-toolexeclibgomimeDATA install-toolexeclibgonetDATA \ install-toolexeclibgomimeDATA install-toolexeclibgonetDATA \
@ -4159,6 +4216,7 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgohashDATA \ uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \ uninstall-toolexeclibgohtmlDATA \
uninstall-toolexeclibgoimageDATA \ uninstall-toolexeclibgoimageDATA \
uninstall-toolexeclibgoimagecolorDATA \
uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \ uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \
uninstall-toolexeclibgologDATA uninstall-toolexeclibgomathDATA \ uninstall-toolexeclibgologDATA uninstall-toolexeclibgomathDATA \
uninstall-toolexeclibgomimeDATA uninstall-toolexeclibgonetDATA \ uninstall-toolexeclibgomimeDATA uninstall-toolexeclibgonetDATA \
@ -4203,6 +4261,7 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \ install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
install-toolexeclibgogoDATA install-toolexeclibgohashDATA \ install-toolexeclibgogoDATA install-toolexeclibgohashDATA \
install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \ install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \
install-toolexeclibgoimagecolorDATA \
install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \ install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \
install-toolexeclibgologDATA install-toolexeclibgomathDATA \ install-toolexeclibgologDATA install-toolexeclibgomathDATA \
install-toolexeclibgomimeDATA install-toolexeclibgonetDATA \ install-toolexeclibgomimeDATA install-toolexeclibgonetDATA \
@ -4234,6 +4293,7 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgohashDATA \ uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \ uninstall-toolexeclibgohtmlDATA \
uninstall-toolexeclibgoimageDATA \ uninstall-toolexeclibgoimageDATA \
uninstall-toolexeclibgoimagecolorDATA \
uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \ uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \
uninstall-toolexeclibgologDATA uninstall-toolexeclibgomathDATA \ uninstall-toolexeclibgologDATA uninstall-toolexeclibgomathDATA \
uninstall-toolexeclibgomimeDATA uninstall-toolexeclibgonetDATA \ uninstall-toolexeclibgomimeDATA uninstall-toolexeclibgonetDATA \
@ -4391,6 +4451,15 @@ crypto/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: crypto/check .PHONY: crypto/check
@go_include@ encoding.lo.dep
encoding.lo.dep: $(go_encoding_files)
$(BUILDDEPS)
encoding.lo: $(go_encoding_files)
$(BUILDPACKAGE)
encoding/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: encoding/check
@go_include@ errors.lo.dep @go_include@ errors.lo.dep
errors.lo.dep: $(go_errors_files) errors.lo.dep: $(go_errors_files)
$(BUILDDEPS) $(BUILDDEPS)
@ -4572,6 +4641,9 @@ strings.lo.dep: $(go_strings_files)
$(BUILDDEPS) $(BUILDDEPS)
strings.lo: $(go_strings_files) strings.lo: $(go_strings_files)
$(BUILDPACKAGE) $(BUILDPACKAGE)
strings/index.lo: $(go_strings_c_files)
@$(MKDIR_P) strings
$(LTCOMPILE) -c -o strings/index.lo $(srcdir)/go/strings/indexbyte.c
strings/check: $(CHECK_DEPS) strings/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: strings/check .PHONY: strings/check
@ -5179,6 +5251,15 @@ image/color/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: image/color/check .PHONY: image/color/check
@go_include@ image/color/palette.lo.dep
image/color/palette.lo.dep: $(go_image_color_palette_files)
$(BUILDDEPS)
image/color/palette.lo: $(go_image_color_palette_files)
$(BUILDPACKAGE)
image/color/palette/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: image/color/palette/check
@go_include@ image/draw.lo.dep @go_include@ image/draw.lo.dep
image/draw.lo.dep: $(go_image_draw_files) image/draw.lo.dep: $(go_image_draw_files)
$(BUILDDEPS) $(BUILDDEPS)
@ -5586,6 +5667,8 @@ bytes.gox: bytes.lo
$(BUILDGOX) $(BUILDGOX)
crypto.gox: crypto.lo crypto.gox: crypto.lo
$(BUILDGOX) $(BUILDGOX)
encoding.gox: encoding.lo
$(BUILDGOX)
errors.gox: errors.lo errors.gox: errors.lo
$(BUILDGOX) $(BUILDGOX)
expvar.gox: expvar.lo expvar.gox: expvar.lo
@ -5783,6 +5866,9 @@ image/jpeg.gox: image/jpeg.lo
image/png.gox: image/png.lo image/png.gox: image/png.lo
$(BUILDGOX) $(BUILDGOX)
image/color/palette.gox: image/color/palette.lo
$(BUILDGOX)
index/suffixarray.gox: index/suffixarray.lo index/suffixarray.gox: index/suffixarray.lo
$(BUILDGOX) $(BUILDGOX)

View file

@ -147,6 +147,9 @@
/* Define to 1 if you have the `mknodat' function. */ /* Define to 1 if you have the `mknodat' function. */
#undef HAVE_MKNODAT #undef HAVE_MKNODAT
/* Define to 1 if you have the <netinet/icmp6.h> header file. */
#undef HAVE_NETINET_ICMP6_H
/* Define to 1 if you have the <netinet/if_ether.h> header file. */ /* Define to 1 if you have the <netinet/if_ether.h> header file. */
#undef HAVE_NETINET_IF_ETHER_H #undef HAVE_NETINET_IF_ETHER_H

22
libgo/configure vendored
View file

@ -659,6 +659,8 @@ LIBGO_IS_SOLARIS_FALSE
LIBGO_IS_SOLARIS_TRUE LIBGO_IS_SOLARIS_TRUE
LIBGO_IS_RTEMS_FALSE LIBGO_IS_RTEMS_FALSE
LIBGO_IS_RTEMS_TRUE LIBGO_IS_RTEMS_TRUE
LIBGO_IS_DRAGONFLY_FALSE
LIBGO_IS_DRAGONFLY_TRUE
LIBGO_IS_OPENBSD_FALSE LIBGO_IS_OPENBSD_FALSE
LIBGO_IS_OPENBSD_TRUE LIBGO_IS_OPENBSD_TRUE
LIBGO_IS_NETBSD_FALSE LIBGO_IS_NETBSD_FALSE
@ -11111,7 +11113,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF cat > conftest.$ac_ext <<_LT_EOF
#line 11114 "configure" #line 11116 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@ -11217,7 +11219,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF cat > conftest.$ac_ext <<_LT_EOF
#line 11220 "configure" #line 11222 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@ -13490,6 +13492,7 @@ is_irix=no
is_linux=no is_linux=no
is_netbsd=no is_netbsd=no
is_openbsd=no is_openbsd=no
is_dragonfly=no
is_rtems=no is_rtems=no
is_solaris=no is_solaris=no
GOOS=unknown GOOS=unknown
@ -13500,6 +13503,7 @@ case ${host} in
*-*-linux*) is_linux=yes; GOOS=linux ;; *-*-linux*) is_linux=yes; GOOS=linux ;;
*-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;; *-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;;
*-*-openbsd*) is_openbsd=yes; GOOS=openbsd ;; *-*-openbsd*) is_openbsd=yes; GOOS=openbsd ;;
*-*-dragonfly*) is_dragonfly=yes; GOOS=dragonfly ;;
*-*-rtems*) is_rtems=yes; GOOS=rtems ;; *-*-rtems*) is_rtems=yes; GOOS=rtems ;;
*-*-solaris2*) is_solaris=yes; GOOS=solaris ;; *-*-solaris2*) is_solaris=yes; GOOS=solaris ;;
esac esac
@ -13551,6 +13555,14 @@ else
LIBGO_IS_OPENBSD_FALSE= LIBGO_IS_OPENBSD_FALSE=
fi fi
if test $is_dragonly = yes; then
LIBGO_IS_DRAGONFLY_TRUE=
LIBGO_IS_DRAGONFLY_FALSE='#'
else
LIBGO_IS_DRAGONFLY_TRUE='#'
LIBGO_IS_DRAGONFLY_FALSE=
fi
if test $is_rtems = yes; then if test $is_rtems = yes; then
LIBGO_IS_RTEMS_TRUE= LIBGO_IS_RTEMS_TRUE=
LIBGO_IS_RTEMS_FALSE='#' LIBGO_IS_RTEMS_FALSE='#'
@ -14600,7 +14612,7 @@ no)
;; ;;
esac esac
for ac_header in sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h for ac_header in sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/icmp6.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h
do : do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@ -15502,6 +15514,10 @@ if test -z "${LIBGO_IS_OPENBSD_TRUE}" && test -z "${LIBGO_IS_OPENBSD_FALSE}"; th
as_fn_error "conditional \"LIBGO_IS_OPENBSD\" was never defined. as_fn_error "conditional \"LIBGO_IS_OPENBSD\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5 Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi fi
if test -z "${LIBGO_IS_DRAGONFLY_TRUE}" && test -z "${LIBGO_IS_DRAGONFLY_FALSE}"; then
as_fn_error "conditional \"LIBGO_IS_DRAGONFLY\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
if test -z "${LIBGO_IS_RTEMS_TRUE}" && test -z "${LIBGO_IS_RTEMS_FALSE}"; then if test -z "${LIBGO_IS_RTEMS_TRUE}" && test -z "${LIBGO_IS_RTEMS_FALSE}"; then
as_fn_error "conditional \"LIBGO_IS_RTEMS\" was never defined. as_fn_error "conditional \"LIBGO_IS_RTEMS\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5 Usually this means the macro was only invoked conditionally." "$LINENO" 5

View file

@ -133,6 +133,7 @@ is_irix=no
is_linux=no is_linux=no
is_netbsd=no is_netbsd=no
is_openbsd=no is_openbsd=no
is_dragonfly=no
is_rtems=no is_rtems=no
is_solaris=no is_solaris=no
GOOS=unknown GOOS=unknown
@ -143,6 +144,7 @@ case ${host} in
*-*-linux*) is_linux=yes; GOOS=linux ;; *-*-linux*) is_linux=yes; GOOS=linux ;;
*-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;; *-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;;
*-*-openbsd*) is_openbsd=yes; GOOS=openbsd ;; *-*-openbsd*) is_openbsd=yes; GOOS=openbsd ;;
*-*-dragonfly*) is_dragonfly=yes; GOOS=dragonfly ;;
*-*-rtems*) is_rtems=yes; GOOS=rtems ;; *-*-rtems*) is_rtems=yes; GOOS=rtems ;;
*-*-solaris2*) is_solaris=yes; GOOS=solaris ;; *-*-solaris2*) is_solaris=yes; GOOS=solaris ;;
esac esac
@ -152,6 +154,7 @@ AM_CONDITIONAL(LIBGO_IS_IRIX, test $is_irix = yes)
AM_CONDITIONAL(LIBGO_IS_LINUX, test $is_linux = yes) AM_CONDITIONAL(LIBGO_IS_LINUX, test $is_linux = yes)
AM_CONDITIONAL(LIBGO_IS_NETBSD, test $is_netbsd = yes) AM_CONDITIONAL(LIBGO_IS_NETBSD, test $is_netbsd = yes)
AM_CONDITIONAL(LIBGO_IS_OPENBSD, test $is_openbsd = yes) AM_CONDITIONAL(LIBGO_IS_OPENBSD, test $is_openbsd = yes)
AM_CONDITIONAL(LIBGO_IS_DRAGONFLY, test $is_dragonly = yes)
AM_CONDITIONAL(LIBGO_IS_RTEMS, test $is_rtems = yes) AM_CONDITIONAL(LIBGO_IS_RTEMS, test $is_rtems = yes)
AM_CONDITIONAL(LIBGO_IS_SOLARIS, test $is_solaris = yes) AM_CONDITIONAL(LIBGO_IS_SOLARIS, test $is_solaris = yes)
AC_SUBST(GOOS) AC_SUBST(GOOS)
@ -471,7 +474,7 @@ no)
;; ;;
esac esac
AC_CHECK_HEADERS(sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h) AC_CHECK_HEADERS(sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/icmp6.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h)
AC_CHECK_HEADERS([linux/filter.h linux/if_addr.h linux/if_ether.h linux/if_tun.h linux/netlink.h linux/rtnetlink.h], [], [], AC_CHECK_HEADERS([linux/filter.h linux/if_addr.h linux/if_ether.h linux/if_tun.h linux/netlink.h linux/rtnetlink.h], [], [],
[#ifdef HAVE_SYS_SOCKET_H [#ifdef HAVE_SYS_SOCKET_H

View file

@ -13,6 +13,7 @@
package tar package tar
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@ -82,9 +83,9 @@ func (fi headerFileInfo) Sys() interface{} { return fi.h }
// Name returns the base name of the file. // Name returns the base name of the file.
func (fi headerFileInfo) Name() string { func (fi headerFileInfo) Name() string {
if fi.IsDir() { if fi.IsDir() {
return path.Clean(fi.h.Name) return path.Base(path.Clean(fi.h.Name))
} }
return fi.h.Name return path.Base(fi.h.Name)
} }
// Mode returns the permission and mode bits for the headerFileInfo. // Mode returns the permission and mode bits for the headerFileInfo.
@ -174,9 +175,29 @@ const (
c_ISSOCK = 0140000 // Socket c_ISSOCK = 0140000 // Socket
) )
// Keywords for the PAX Extended Header
const (
paxAtime = "atime"
paxCharset = "charset"
paxComment = "comment"
paxCtime = "ctime" // please note that ctime is not a valid pax header.
paxGid = "gid"
paxGname = "gname"
paxLinkpath = "linkpath"
paxMtime = "mtime"
paxPath = "path"
paxSize = "size"
paxUid = "uid"
paxUname = "uname"
paxNone = ""
)
// FileInfoHeader creates a partially-populated Header from fi. // FileInfoHeader creates a partially-populated Header from fi.
// If fi describes a symlink, FileInfoHeader records link as the link target. // If fi describes a symlink, FileInfoHeader records link as the link target.
// If fi describes a directory, a slash is appended to the name. // If fi describes a directory, a slash is appended to the name.
// Because os.FileInfo's Name method returns only the base name of
// the file it describes, it may be necessary to modify the Name field
// of the returned header to provide the full path name of the file.
func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
if fi == nil { if fi == nil {
return nil, errors.New("tar: FileInfo is nil") return nil, errors.New("tar: FileInfo is nil")
@ -257,3 +278,25 @@ func (sp *slicer) next(n int) (b []byte) {
b, *sp = s[0:n], s[n:] b, *sp = s[0:n], s[n:]
return return
} }
func isASCII(s string) bool {
for _, c := range s {
if c >= 0x80 {
return false
}
}
return true
}
func toASCII(s string) string {
if isASCII(s) {
return s
}
var buf bytes.Buffer
for _, c := range s {
if c < 0x80 {
buf.WriteByte(byte(c))
}
}
return buf.String()
}

View file

@ -95,45 +95,45 @@ func (tr *Reader) Next() (*Header, error) {
func mergePAX(hdr *Header, headers map[string]string) error { func mergePAX(hdr *Header, headers map[string]string) error {
for k, v := range headers { for k, v := range headers {
switch k { switch k {
case "path": case paxPath:
hdr.Name = v hdr.Name = v
case "linkpath": case paxLinkpath:
hdr.Linkname = v hdr.Linkname = v
case "gname": case paxGname:
hdr.Gname = v hdr.Gname = v
case "uname": case paxUname:
hdr.Uname = v hdr.Uname = v
case "uid": case paxUid:
uid, err := strconv.ParseInt(v, 10, 0) uid, err := strconv.ParseInt(v, 10, 0)
if err != nil { if err != nil {
return err return err
} }
hdr.Uid = int(uid) hdr.Uid = int(uid)
case "gid": case paxGid:
gid, err := strconv.ParseInt(v, 10, 0) gid, err := strconv.ParseInt(v, 10, 0)
if err != nil { if err != nil {
return err return err
} }
hdr.Gid = int(gid) hdr.Gid = int(gid)
case "atime": case paxAtime:
t, err := parsePAXTime(v) t, err := parsePAXTime(v)
if err != nil { if err != nil {
return err return err
} }
hdr.AccessTime = t hdr.AccessTime = t
case "mtime": case paxMtime:
t, err := parsePAXTime(v) t, err := parsePAXTime(v)
if err != nil { if err != nil {
return err return err
} }
hdr.ModTime = t hdr.ModTime = t
case "ctime": case paxCtime:
t, err := parsePAXTime(v) t, err := parsePAXTime(v)
if err != nil { if err != nil {
return err return err
} }
hdr.ChangeTime = t hdr.ChangeTime = t
case "size": case paxSize:
size, err := strconv.ParseInt(v, 10, 0) size, err := strconv.ParseInt(v, 10, 0)
if err != nil { if err != nil {
return err return err
@ -243,13 +243,15 @@ func (tr *Reader) octal(b []byte) int64 {
return x return x
} }
// Removing leading spaces. // Because unused fields are filled with NULs, we need
for len(b) > 0 && b[0] == ' ' { // to skip leading NULs. Fields may also be padded with
b = b[1:] // spaces or NULs.
} // So we remove leading and trailing NULs and spaces to
// Removing trailing NULs and spaces. // be sure.
for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') { b = bytes.Trim(b, " \x00")
b = b[0 : len(b)-1]
if len(b) == 0 {
return 0
} }
x, err := strconv.ParseUint(cString(b), 8, 64) x, err := strconv.ParseUint(cString(b), 8, 64)
if err != nil { if err != nil {

View file

@ -142,6 +142,25 @@ var untarTests = []*untarTest{
}, },
}, },
}, },
{
file: "testdata/nil-uid.tar", // golang.org/issue/5290
headers: []*Header{
{
Name: "P1050238.JPG.log",
Mode: 0664,
Uid: 0,
Gid: 0,
Size: 14,
ModTime: time.Unix(1365454838, 0),
Typeflag: TypeReg,
Linkname: "",
Uname: "eyefi",
Gname: "eyefi",
Devmajor: 0,
Devminor: 0,
},
},
},
} }
func TestReader(t *testing.T) { func TestReader(t *testing.T) {
@ -152,6 +171,7 @@ testLoop:
t.Errorf("test %d: Unexpected error: %v", i, err) t.Errorf("test %d: Unexpected error: %v", i, err)
continue continue
} }
defer f.Close()
tr := NewReader(f) tr := NewReader(f)
for j, header := range test.headers { for j, header := range test.headers {
hdr, err := tr.Next() hdr, err := tr.Next()
@ -172,7 +192,6 @@ testLoop:
if hdr != nil || err != nil { if hdr != nil || err != nil {
t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err) t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err)
} }
f.Close()
} }
} }

View file

@ -8,7 +8,9 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"reflect" "reflect"
"strings"
"testing" "testing"
"time" "time"
) )
@ -249,7 +251,14 @@ func TestHeaderRoundTrip(t *testing.T) {
t.Error(err) t.Error(err)
continue continue
} }
if got, want := h2.Name, g.h.Name; got != want { if strings.Contains(fi.Name(), "/") {
t.Errorf("FileInfo of %q contains slash: %q", g.h.Name, fi.Name())
}
name := path.Base(g.h.Name)
if fi.IsDir() {
name += "/"
}
if got, want := h2.Name, name; got != want {
t.Errorf("i=%d: Name: got %v, want %v", i, got, want) t.Errorf("i=%d: Name: got %v, want %v", i, got, want)
} }
if got, want := h2.Size, g.h.Size; got != want { if got, want := h2.Size, g.h.Size; got != want {

Binary file not shown.

View file

@ -24,6 +24,7 @@ var (
ErrFieldTooLong = errors.New("archive/tar: header field too long") ErrFieldTooLong = errors.New("archive/tar: header field too long")
ErrWriteAfterClose = errors.New("archive/tar: write after close") ErrWriteAfterClose = errors.New("archive/tar: write after close")
errNameTooLong = errors.New("archive/tar: name too long") errNameTooLong = errors.New("archive/tar: name too long")
errInvalidHeader = errors.New("archive/tar: header field too long or contains invalid values")
) )
// A Writer provides sequential writing of a tar archive in POSIX.1 format. // A Writer provides sequential writing of a tar archive in POSIX.1 format.
@ -37,6 +38,7 @@ type Writer struct {
pad int64 // amount of padding to write after current file entry pad int64 // amount of padding to write after current file entry
closed bool closed bool
usedBinary bool // whether the binary numeric field extension was used usedBinary bool // whether the binary numeric field extension was used
preferPax bool // use pax header instead of binary numeric header
} }
// NewWriter creates a new Writer writing to w. // NewWriter creates a new Writer writing to w.
@ -65,16 +67,23 @@ func (tw *Writer) Flush() error {
} }
// Write s into b, terminating it with a NUL if there is room. // Write s into b, terminating it with a NUL if there is room.
func (tw *Writer) cString(b []byte, s string) { // If the value is too long for the field and allowPax is true add a paxheader record instead
func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s)
if needsPaxHeader {
paxHeaders[paxKeyword] = s
return
}
if len(s) > len(b) { if len(s) > len(b) {
if tw.err == nil { if tw.err == nil {
tw.err = ErrFieldTooLong tw.err = ErrFieldTooLong
} }
return return
} }
copy(b, s) ascii := toASCII(s)
if len(s) < len(b) { copy(b, ascii)
b[len(s)] = 0 if len(ascii) < len(b) {
b[len(ascii)] = 0
} }
} }
@ -85,17 +94,27 @@ func (tw *Writer) octal(b []byte, x int64) {
for len(s)+1 < len(b) { for len(s)+1 < len(b) {
s = "0" + s s = "0" + s
} }
tw.cString(b, s) tw.cString(b, s, false, paxNone, nil)
} }
// Write x into b, either as octal or as binary (GNUtar/star extension). // Write x into b, either as octal or as binary (GNUtar/star extension).
func (tw *Writer) numeric(b []byte, x int64) { // If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead
func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) {
// Try octal first. // Try octal first.
s := strconv.FormatInt(x, 8) s := strconv.FormatInt(x, 8)
if len(s) < len(b) { if len(s) < len(b) {
tw.octal(b, x) tw.octal(b, x)
return return
} }
// If it is too long for octal, and pax is preferred, use a pax header
if allowPax && tw.preferPax {
tw.octal(b, 0)
s := strconv.FormatInt(x, 10)
paxHeaders[paxKeyword] = s
return
}
// Too big: use binary (big-endian). // Too big: use binary (big-endian).
tw.usedBinary = true tw.usedBinary = true
for i := len(b) - 1; x > 0 && i >= 0; i-- { for i := len(b) - 1; x > 0 && i >= 0; i-- {
@ -115,6 +134,15 @@ var (
// WriteHeader calls Flush if it is not the first header. // WriteHeader calls Flush if it is not the first header.
// Calling after a Close will return ErrWriteAfterClose. // Calling after a Close will return ErrWriteAfterClose.
func (tw *Writer) WriteHeader(hdr *Header) error { func (tw *Writer) WriteHeader(hdr *Header) error {
return tw.writeHeader(hdr, true)
}
// WriteHeader writes hdr and prepares to accept the file's contents.
// WriteHeader calls Flush if it is not the first header.
// Calling after a Close will return ErrWriteAfterClose.
// As this method is called internally by writePax header to allow it to
// suppress writing the pax header.
func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
if tw.closed { if tw.closed {
return ErrWriteAfterClose return ErrWriteAfterClose
} }
@ -124,31 +152,21 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
if tw.err != nil { if tw.err != nil {
return tw.err return tw.err
} }
// Decide whether or not to use PAX extensions
// a map to hold pax header records, if any are needed
paxHeaders := make(map[string]string)
// TODO(shanemhansen): we might want to use PAX headers for // TODO(shanemhansen): we might want to use PAX headers for
// subsecond time resolution, but for now let's just capture // subsecond time resolution, but for now let's just capture
// the long name/long symlink use case. // too long fields or non ascii characters
suffix := hdr.Name
prefix := ""
if len(hdr.Name) > fileNameSize || len(hdr.Linkname) > fileNameSize {
var err error
prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
// Either we were unable to pack the long name into ustar format
// or the link name is too long; use PAX headers.
if err == errNameTooLong || len(hdr.Linkname) > fileNameSize {
if err := tw.writePAXHeader(hdr); err != nil {
return err
}
} else if err != nil {
return err
}
}
tw.nb = int64(hdr.Size)
tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two
header := make([]byte, blockSize) header := make([]byte, blockSize)
s := slicer(header) s := slicer(header)
tw.cString(s.next(fileNameSize), suffix)
// keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
pathHeaderBytes := s.next(fileNameSize)
tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders)
// Handle out of range ModTime carefully. // Handle out of range ModTime carefully.
var modTime int64 var modTime int64
@ -156,27 +174,55 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
modTime = hdr.ModTime.Unix() modTime = hdr.ModTime.Unix()
} }
tw.octal(s.next(8), hdr.Mode) // 100:108 tw.octal(s.next(8), hdr.Mode) // 100:108
tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116
tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124
tw.numeric(s.next(12), hdr.Size) // 124:136 tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders) // 124:136
tw.numeric(s.next(12), modTime) // 136:148 tw.numeric(s.next(12), modTime, false, paxNone, nil) // 136:148 --- consider using pax for finer granularity
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
tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
copy(s.next(8), []byte("ustar\x0000")) // 257:265 tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders)
tw.cString(s.next(32), hdr.Uname) // 265:297
tw.cString(s.next(32), hdr.Gname) // 297:329 copy(s.next(8), []byte("ustar\x0000")) // 257:265
tw.numeric(s.next(8), hdr.Devmajor) // 329:337 tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297
tw.numeric(s.next(8), hdr.Devminor) // 337:345 tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329
tw.cString(s.next(155), prefix) // 345:500 tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil) // 329:337
tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil) // 337:345
// keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax
prefixHeaderBytes := s.next(155)
tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500 prefix
// Use the GNU magic instead of POSIX magic if we used any GNU extensions. // Use the GNU magic instead of POSIX magic if we used any GNU extensions.
if tw.usedBinary { if tw.usedBinary {
copy(header[257:265], []byte("ustar \x00")) copy(header[257:265], []byte("ustar \x00"))
} }
// Use the ustar magic if we used ustar long names.
if len(prefix) > 0 { _, paxPathUsed := paxHeaders[paxPath]
copy(header[257:265], []byte("ustar\000")) // try to use a ustar header when only the name is too long
if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed {
suffix := hdr.Name
prefix := ""
if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) {
var err error
prefix, suffix, err = tw.splitUSTARLongName(hdr.Name)
if err == nil {
// ok we can use a ustar long name instead of pax, now correct the fields
// remove the path field from the pax header. this will suppress the pax header
delete(paxHeaders, paxPath)
// update the path fields
tw.cString(pathHeaderBytes, suffix, false, paxNone, nil)
tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil)
// Use the ustar magic if we used ustar long names.
if len(prefix) > 0 {
copy(header[257:265], []byte("ustar\000"))
}
}
}
} }
// The chksum field is terminated by a NUL and a space. // The chksum field is terminated by a NUL and a space.
@ -190,8 +236,18 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
return tw.err return tw.err
} }
_, tw.err = tw.w.Write(header) if len(paxHeaders) > 0 {
if !allowPax {
return errInvalidHeader
}
if err := tw.writePAXHeader(hdr, paxHeaders); err != nil {
return err
}
}
tw.nb = int64(hdr.Size)
tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize
_, tw.err = tw.w.Write(header)
return tw.err return tw.err
} }
@ -207,8 +263,11 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er
length-- length--
} }
i := strings.LastIndex(name[:length], "/") i := strings.LastIndex(name[:length], "/")
nlen := length - i - 1 // nlen contains the resulting length in the name field.
if i <= 0 || nlen > fileNameSize || nlen == 0 { // plen contains the resulting length in the prefix field.
nlen := len(name) - i - 1
plen := i
if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize {
err = errNameTooLong err = errNameTooLong
return return
} }
@ -218,7 +277,7 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er
// writePaxHeader writes an extended pax header to the // writePaxHeader writes an extended pax header to the
// archive. // archive.
func (tw *Writer) writePAXHeader(hdr *Header) error { func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error {
// Prepare extended header // Prepare extended header
ext := new(Header) ext := new(Header)
ext.Typeflag = TypeXHeader ext.Typeflag = TypeXHeader
@ -229,18 +288,23 @@ func (tw *Writer) writePAXHeader(hdr *Header) error {
// with the current pid. // with the current pid.
pid := os.Getpid() pid := os.Getpid()
dir, file := path.Split(hdr.Name) dir, file := path.Split(hdr.Name)
ext.Name = path.Join(dir, fullName := path.Join(dir,
fmt.Sprintf("PaxHeaders.%d", pid), file)[0:100] fmt.Sprintf("PaxHeaders.%d", pid), file)
ascii := toASCII(fullName)
if len(ascii) > 100 {
ascii = ascii[:100]
}
ext.Name = ascii
// Construct the body // Construct the body
var buf bytes.Buffer var buf bytes.Buffer
if len(hdr.Name) > fileNameSize {
fmt.Fprint(&buf, paxHeader("path="+hdr.Name)) for k, v := range paxHeaders {
} fmt.Fprint(&buf, paxHeader(k+"="+v))
if len(hdr.Linkname) > fileNameSize {
fmt.Fprint(&buf, paxHeader("linkpath="+hdr.Linkname))
} }
ext.Size = int64(len(buf.Bytes())) ext.Size = int64(len(buf.Bytes()))
if err := tw.WriteHeader(ext); err != nil { if err := tw.writeHeader(ext, false); err != nil {
return err return err
} }
if _, err := tw.Write(buf.Bytes()); err != nil { if _, err := tw.Write(buf.Bytes()); err != nil {

View file

@ -243,15 +243,110 @@ func TestPax(t *testing.T) {
} }
} }
func TestPaxSymlink(t *testing.T) {
// Create an archive with a large linkname
fileinfo, err := os.Stat("testdata/small.txt")
if err != nil {
t.Fatal(err)
}
hdr, err := FileInfoHeader(fileinfo, "")
hdr.Typeflag = TypeSymlink
if err != nil {
t.Fatalf("os.Stat:1 %v", err)
}
// Force a PAX long linkname to be written
longLinkname := strings.Repeat("1234567890/1234567890", 10)
hdr.Linkname = longLinkname
hdr.Size = 0
var buf bytes.Buffer
writer := NewWriter(&buf)
if err := writer.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
// Simple test to make sure PAX extensions are in effect
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
t.Fatal("Expected at least one PAX header to be written.")
}
// Test that we can get a long name back out of the archive.
reader := NewReader(&buf)
hdr, err = reader.Next()
if err != nil {
t.Fatal(err)
}
if hdr.Linkname != longLinkname {
t.Fatal("Couldn't recover long link name")
}
}
func TestPaxNonAscii(t *testing.T) {
// Create an archive with non ascii. These should trigger a pax header
// because pax headers have a defined utf-8 encoding.
fileinfo, err := os.Stat("testdata/small.txt")
if err != nil {
t.Fatal(err)
}
hdr, err := FileInfoHeader(fileinfo, "")
if err != nil {
t.Fatalf("os.Stat:1 %v", err)
}
// some sample data
chineseFilename := "文件名"
chineseGroupname := "組"
chineseUsername := "用戶名"
hdr.Name = chineseFilename
hdr.Gname = chineseGroupname
hdr.Uname = chineseUsername
contents := strings.Repeat(" ", int(hdr.Size))
var buf bytes.Buffer
writer := NewWriter(&buf)
if err := writer.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
if _, err = writer.Write([]byte(contents)); err != nil {
t.Fatal(err)
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
// Simple test to make sure PAX extensions are in effect
if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) {
t.Fatal("Expected at least one PAX header to be written.")
}
// Test that we can get a long name back out of the archive.
reader := NewReader(&buf)
hdr, err = reader.Next()
if err != nil {
t.Fatal(err)
}
if hdr.Name != chineseFilename {
t.Fatal("Couldn't recover unicode name")
}
if hdr.Gname != chineseGroupname {
t.Fatal("Couldn't recover unicode group")
}
if hdr.Uname != chineseUsername {
t.Fatal("Couldn't recover unicode user")
}
}
func TestPAXHeader(t *testing.T) { func TestPAXHeader(t *testing.T) {
medName := strings.Repeat("CD", 50) medName := strings.Repeat("CD", 50)
longName := strings.Repeat("AB", 100) longName := strings.Repeat("AB", 100)
paxTests := [][2]string{ paxTests := [][2]string{
{"name=/etc/hosts", "19 name=/etc/hosts\n"}, {paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"},
{"a=b", "6 a=b\n"}, // Single digit length {"a=b", "6 a=b\n"}, // Single digit length
{"a=names", "11 a=names\n"}, // Test case involving carries {"a=names", "11 a=names\n"}, // Test case involving carries
{"name=" + longName, fmt.Sprintf("210 name=%s\n", longName)}, {paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)},
{"name=" + medName, fmt.Sprintf("110 name=%s\n", medName)}} {paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}}
for _, test := range paxTests { for _, test := range paxTests {
key, expected := test[0], test[1] key, expected := test[0], test[1]
@ -260,3 +355,39 @@ func TestPAXHeader(t *testing.T) {
} }
} }
} }
func TestUSTARLongName(t *testing.T) {
// Create an archive with a path that failed to split with USTAR extension in previous versions.
fileinfo, err := os.Stat("testdata/small.txt")
if err != nil {
t.Fatal(err)
}
hdr, err := FileInfoHeader(fileinfo, "")
hdr.Typeflag = TypeDir
if err != nil {
t.Fatalf("os.Stat:1 %v", err)
}
// Force a PAX long name to be written. The name was taken from a practical example
// that fails and replaced ever char through numbers to anonymize the sample.
longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/"
hdr.Name = longName
hdr.Size = 0
var buf bytes.Buffer
writer := NewWriter(&buf)
if err := writer.WriteHeader(hdr); err != nil {
t.Fatal(err)
}
if err := writer.Close(); err != nil {
t.Fatal(err)
}
// Test that we can get a long name back out of the archive.
reader := NewReader(&buf)
hdr, err = reader.Next()
if err != nil {
t.Fatal(err)
}
if hdr.Name != longName {
t.Fatal("Couldn't recover long name")
}
}

View file

@ -6,13 +6,11 @@ package zip
import ( import (
"bufio" "bufio"
"compress/flate"
"encoding/binary" "encoding/binary"
"errors" "errors"
"hash" "hash"
"hash/crc32" "hash/crc32"
"io" "io"
"io/ioutil"
"os" "os"
) )
@ -116,6 +114,19 @@ func (rc *ReadCloser) Close() error {
return rc.f.Close() return rc.f.Close()
} }
// DataOffset returns the offset of the file's possibly-compressed
// data, relative to the beginning of the zip file.
//
// Most callers should instead use Open, which transparently
// decompresses data and verifies checksums.
func (f *File) DataOffset() (offset int64, err error) {
bodyOffset, err := f.findBodyOffset()
if err != nil {
return
}
return f.headerOffset + bodyOffset, nil
}
// Open returns a ReadCloser that provides access to the File's contents. // Open returns a ReadCloser that provides access to the File's contents.
// Multiple files may be read concurrently. // Multiple files may be read concurrently.
func (f *File) Open() (rc io.ReadCloser, err error) { func (f *File) Open() (rc io.ReadCloser, err error) {
@ -125,15 +136,12 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
} }
size := int64(f.CompressedSize64) size := int64(f.CompressedSize64)
r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size) r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
switch f.Method { dcomp := decompressor(f.Method)
case Store: // (no compression) if dcomp == nil {
rc = ioutil.NopCloser(r)
case Deflate:
rc = flate.NewReader(r)
default:
err = ErrAlgorithm err = ErrAlgorithm
return return
} }
rc = dcomp(r)
var desr io.Reader var desr io.Reader
if f.hasDataDescriptor() { if f.hasDataDescriptor() {
desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen)
@ -184,9 +192,8 @@ func (r *checksumReader) Close() error { return r.rc.Close() }
// findBodyOffset does the minimum work to verify the file has a header // findBodyOffset does the minimum work to verify the file has a header
// and returns the file body offset. // and returns the file body offset.
func (f *File) findBodyOffset() (int64, error) { func (f *File) findBodyOffset() (int64, error) {
r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset)
var buf [fileHeaderLen]byte var buf [fileHeaderLen]byte
if _, err := io.ReadFull(r, buf[:]); err != nil { if _, err := f.zipr.ReadAt(buf[:], f.headerOffset); err != nil {
return 0, err return 0, err
} }
b := readBuf(buf[:]) b := readBuf(buf[:])

View file

@ -276,6 +276,7 @@ func readTestZip(t *testing.T, zt ZipTest) {
var rc *ReadCloser var rc *ReadCloser
rc, err = OpenReader(filepath.Join("testdata", zt.Name)) rc, err = OpenReader(filepath.Join("testdata", zt.Name))
if err == nil { if err == nil {
defer rc.Close()
z = &rc.Reader z = &rc.Reader
} }
} }

View file

@ -0,0 +1,71 @@
// Copyright 2010 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 zip
import (
"compress/flate"
"io"
"io/ioutil"
"sync"
)
// A Compressor returns a compressing writer, writing to the
// provided writer. On Close, any pending data should be flushed.
type Compressor func(io.Writer) (io.WriteCloser, error)
// Decompressor is a function that wraps a Reader with a decompressing Reader.
// The decompressed ReadCloser is returned to callers who open files from
// within the archive. These callers are responsible for closing this reader
// when they're finished reading.
type Decompressor func(io.Reader) io.ReadCloser
var (
mu sync.RWMutex // guards compressor and decompressor maps
compressors = map[uint16]Compressor{
Store: func(w io.Writer) (io.WriteCloser, error) { return &nopCloser{w}, nil },
Deflate: func(w io.Writer) (io.WriteCloser, error) { return flate.NewWriter(w, 5) },
}
decompressors = map[uint16]Decompressor{
Store: ioutil.NopCloser,
Deflate: flate.NewReader,
}
)
// RegisterDecompressor allows custom decompressors for a specified method ID.
func RegisterDecompressor(method uint16, d Decompressor) {
mu.Lock()
defer mu.Unlock()
if _, ok := decompressors[method]; ok {
panic("decompressor already registered")
}
decompressors[method] = d
}
// RegisterCompressor registers custom compressors for a specified method ID.
// The common methods Store and Deflate are built in.
func RegisterCompressor(method uint16, comp Compressor) {
mu.Lock()
defer mu.Unlock()
if _, ok := compressors[method]; ok {
panic("compressor already registered")
}
compressors[method] = comp
}
func compressor(method uint16) Compressor {
mu.RLock()
defer mu.RUnlock()
return compressors[method]
}
func decompressor(method uint16) Decompressor {
mu.RLock()
defer mu.RUnlock()
return decompressors[method]
}

View file

@ -21,6 +21,7 @@ package zip
import ( import (
"os" "os"
"path"
"time" "time"
) )
@ -99,7 +100,7 @@ type headerFileInfo struct {
fh *FileHeader fh *FileHeader
} }
func (fi headerFileInfo) Name() string { return fi.fh.Name } func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) }
func (fi headerFileInfo) Size() int64 { func (fi headerFileInfo) Size() int64 {
if fi.fh.UncompressedSize64 > 0 { if fi.fh.UncompressedSize64 > 0 {
return int64(fi.fh.UncompressedSize64) return int64(fi.fh.UncompressedSize64)
@ -113,6 +114,9 @@ func (fi headerFileInfo) Sys() interface{} { return fi.fh }
// FileInfoHeader creates a partially-populated FileHeader from an // FileInfoHeader creates a partially-populated FileHeader from an
// os.FileInfo. // os.FileInfo.
// Because os.FileInfo's Name method returns only the base name of
// the file it describes, it may be necessary to modify the Name field
// of the returned header to provide the full path name of the file.
func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
size := fi.Size() size := fi.Size()
fh := &FileHeader{ fh := &FileHeader{

View file

@ -6,7 +6,6 @@ package zip
import ( import (
"bufio" "bufio"
"compress/flate"
"encoding/binary" "encoding/binary"
"errors" "errors"
"hash" "hash"
@ -198,18 +197,15 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
compCount: &countWriter{w: w.cw}, compCount: &countWriter{w: w.cw},
crc32: crc32.NewIEEE(), crc32: crc32.NewIEEE(),
} }
switch fh.Method { comp := compressor(fh.Method)
case Store: if comp == nil {
fw.comp = nopCloser{fw.compCount}
case Deflate:
var err error
fw.comp, err = flate.NewWriter(fw.compCount, 5)
if err != nil {
return nil, err
}
default:
return nil, ErrAlgorithm return nil, ErrAlgorithm
} }
var err error
fw.comp, err = comp(fw.compCount)
if err != nil {
return nil, err
}
fw.rawCount = &countWriter{w: fw.comp} fw.rawCount = &countWriter{w: fw.comp}
h := &header{ h := &header{

View file

@ -9,22 +9,24 @@ package zip
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"hash"
"io" "io"
"io/ioutil" "io/ioutil"
"sort"
"strings" "strings"
"testing" "testing"
"time" "time"
) )
func TestOver65kFiles(t *testing.T) { func TestOver65kFiles(t *testing.T) {
if testing.Short() {
t.Skip("slow test; skipping")
}
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
w := NewWriter(buf) w := NewWriter(buf)
const nFiles = (1 << 16) + 42 const nFiles = (1 << 16) + 42
for i := 0; i < nFiles; i++ { for i := 0; i < nFiles; i++ {
_, err := w.Create(fmt.Sprintf("%d.dat", i)) _, err := w.CreateHeader(&FileHeader{
Name: fmt.Sprintf("%d.dat", i),
Method: Store, // avoid Issue 6136 and Issue 6138
})
if err != nil { if err != nil {
t.Fatalf("creating file %d: %v", i, err) t.Fatalf("creating file %d: %v", i, err)
} }
@ -105,29 +107,156 @@ func TestFileHeaderRoundTrip64(t *testing.T) {
testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t) testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t)
} }
type repeatedByte struct {
off int64
b byte
n int64
}
// rleBuffer is a run-length-encoded byte buffer.
// It's an io.Writer (like a bytes.Buffer) and also an io.ReaderAt,
// allowing random-access reads.
type rleBuffer struct {
buf []repeatedByte
}
func (r *rleBuffer) Size() int64 {
if len(r.buf) == 0 {
return 0
}
last := &r.buf[len(r.buf)-1]
return last.off + last.n
}
func (r *rleBuffer) Write(p []byte) (n int, err error) {
var rp *repeatedByte
if len(r.buf) > 0 {
rp = &r.buf[len(r.buf)-1]
// Fast path, if p is entirely the same byte repeated.
if lastByte := rp.b; len(p) > 0 && p[0] == lastByte {
all := true
for _, b := range p {
if b != lastByte {
all = false
break
}
}
if all {
rp.n += int64(len(p))
return len(p), nil
}
}
}
for _, b := range p {
if rp == nil || rp.b != b {
r.buf = append(r.buf, repeatedByte{r.Size(), b, 1})
rp = &r.buf[len(r.buf)-1]
} else {
rp.n++
}
}
return len(p), nil
}
func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) {
if len(p) == 0 {
return
}
skipParts := sort.Search(len(r.buf), func(i int) bool {
part := &r.buf[i]
return part.off+part.n > off
})
parts := r.buf[skipParts:]
if len(parts) > 0 {
skipBytes := off - parts[0].off
for len(parts) > 0 {
part := parts[0]
for i := skipBytes; i < part.n; i++ {
if n == len(p) {
return
}
p[n] = part.b
n++
}
parts = parts[1:]
skipBytes = 0
}
}
if n != len(p) {
err = io.ErrUnexpectedEOF
}
return
}
// Just testing the rleBuffer used in the Zip64 test above. Not used by the zip code.
func TestRLEBuffer(t *testing.T) {
b := new(rleBuffer)
var all []byte
writes := []string{"abcdeee", "eeeeeee", "eeeefghaaiii"}
for _, w := range writes {
b.Write([]byte(w))
all = append(all, w...)
}
if len(b.buf) != 10 {
t.Fatalf("len(b.buf) = %d; want 10", len(b.buf))
}
for i := 0; i < len(all); i++ {
for j := 0; j < len(all)-i; j++ {
buf := make([]byte, j)
n, err := b.ReadAt(buf, int64(i))
if err != nil || n != len(buf) {
t.Errorf("ReadAt(%d, %d) = %d, %v; want %d, nil", i, j, n, err, len(buf))
}
if !bytes.Equal(buf, all[i:i+j]) {
t.Errorf("ReadAt(%d, %d) = %q; want %q", i, j, buf, all[i:i+j])
}
}
}
}
// fakeHash32 is a dummy Hash32 that always returns 0.
type fakeHash32 struct {
hash.Hash32
}
func (fakeHash32) Write(p []byte) (int, error) { return len(p), nil }
func (fakeHash32) Sum32() uint32 { return 0 }
func TestZip64(t *testing.T) { func TestZip64(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("slow test; skipping") t.Skip("slow test; skipping")
} }
const size = 1 << 32 // before the "END\n" part
testZip64(t, size)
}
func testZip64(t testing.TB, size int64) {
const chunkSize = 1024
chunks := int(size / chunkSize)
// write 2^32 bytes plus "END\n" to a zip file // write 2^32 bytes plus "END\n" to a zip file
buf := new(bytes.Buffer) buf := new(rleBuffer)
w := NewWriter(buf) w := NewWriter(buf)
f, err := w.Create("huge.txt") f, err := w.CreateHeader(&FileHeader{
Name: "huge.txt",
Method: Store,
})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
chunk := make([]byte, 1024) f.(*fileWriter).crc32 = fakeHash32{}
chunk := make([]byte, chunkSize)
for i := range chunk { for i := range chunk {
chunk[i] = '.' chunk[i] = '.'
} }
chunk[len(chunk)-1] = '\n' for i := 0; i < chunks; i++ {
end := []byte("END\n")
for i := 0; i < (1<<32)/1024; i++ {
_, err := f.Write(chunk) _, err := f.Write(chunk)
if err != nil { if err != nil {
t.Fatal("write chunk:", err) t.Fatal("write chunk:", err)
} }
} }
end := []byte("END\n")
_, err = f.Write(end) _, err = f.Write(end)
if err != nil { if err != nil {
t.Fatal("write end:", err) t.Fatal("write end:", err)
@ -137,7 +266,7 @@ func TestZip64(t *testing.T) {
} }
// read back zip file and check that we get to the end of it // read back zip file and check that we get to the end of it
r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())) r, err := NewReader(buf, int64(buf.Size()))
if err != nil { if err != nil {
t.Fatal("reader:", err) t.Fatal("reader:", err)
} }
@ -146,7 +275,8 @@ func TestZip64(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("opening:", err) t.Fatal("opening:", err)
} }
for i := 0; i < (1<<32)/1024; i++ { rc.(*checksumReader).hash = fakeHash32{}
for i := 0; i < chunks; i++ {
_, err := io.ReadFull(rc, chunk) _, err := io.ReadFull(rc, chunk)
if err != nil { if err != nil {
t.Fatal("read:", err) t.Fatal("read:", err)
@ -163,11 +293,13 @@ func TestZip64(t *testing.T) {
if err != nil { if err != nil {
t.Fatal("closing:", err) t.Fatal("closing:", err)
} }
if got, want := f0.UncompressedSize, uint32(uint32max); got != want { if size == 1<<32 {
t.Errorf("UncompressedSize %d, want %d", got, want) if got, want := f0.UncompressedSize, uint32(uint32max); got != want {
t.Errorf("UncompressedSize %d, want %d", got, want)
}
} }
if got, want := f0.UncompressedSize64, (1<<32)+uint64(len(end)); got != want { if got, want := f0.UncompressedSize64, uint64(size)+uint64(len(end)); got != want {
t.Errorf("UncompressedSize64 %d, want %d", got, want) t.Errorf("UncompressedSize64 %d, want %d", got, want)
} }
} }
@ -253,3 +385,11 @@ func TestZeroLengthHeader(t *testing.T) {
} }
testValidHeader(&h, t) testValidHeader(&h, t)
} }
// Just benchmarking how fast the Zip64 test above is. Not related to
// our zip performance, since the test above disabled CRC32 and flate.
func BenchmarkZip64Test(b *testing.B) {
for i := 0; i < b.N; i++ {
testZip64(b, 1<<26)
}
}

View file

@ -51,12 +51,9 @@ func NewReaderSize(rd io.Reader, size int) *Reader {
if size < minReadBufferSize { if size < minReadBufferSize {
size = minReadBufferSize size = minReadBufferSize
} }
return &Reader{ r := new(Reader)
buf: make([]byte, size), r.reset(make([]byte, size), rd)
rd: rd, return r
lastByte: -1,
lastRuneSize: -1,
}
} }
// NewReader returns a new Reader whose buffer has the default size. // NewReader returns a new Reader whose buffer has the default size.
@ -64,6 +61,21 @@ func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize) return NewReaderSize(rd, defaultBufSize)
} }
// Reset discards any buffered data, resets all state, and switches
// the buffered reader to read from r.
func (b *Reader) Reset(r io.Reader) {
b.reset(b.buf, r)
}
func (b *Reader) reset(buf []byte, r io.Reader) {
*b = Reader{
buf: buf,
rd: r,
lastByte: -1,
lastRuneSize: -1,
}
}
var errNegativeRead = errors.New("bufio: reader returned negative count from Read") var errNegativeRead = errors.New("bufio: reader returned negative count from Read")
// fill reads a new chunk into the buffer. // fill reads a new chunk into the buffer.
@ -234,7 +246,7 @@ func (b *Reader) Buffered() int { return b.w - b.r }
// ReadSlice reads until the first occurrence of delim in the input, // ReadSlice reads until the first occurrence of delim in the input,
// returning a slice pointing at the bytes in the buffer. // returning a slice pointing at the bytes in the buffer.
// The bytes stop being valid at the next read call. // The bytes stop being valid at the next read.
// If ReadSlice encounters an error before finding a delimiter, // If ReadSlice encounters an error before finding a delimiter,
// it returns all the data in the buffer and the error itself (often io.EOF). // it returns all the data in the buffer and the error itself (often io.EOF).
// ReadSlice fails with error ErrBufferFull if the buffer fills without a delim. // ReadSlice fails with error ErrBufferFull if the buffer fills without a delim.
@ -381,7 +393,8 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err error) {
// For simple uses, a Scanner may be more convenient. // For simple uses, a Scanner may be more convenient.
func (b *Reader) ReadString(delim byte) (line string, err error) { func (b *Reader) ReadString(delim byte) (line string, err error) {
bytes, err := b.ReadBytes(delim) bytes, err := b.ReadBytes(delim)
return string(bytes), err line = string(bytes)
return line, err
} }
// WriteTo implements io.WriterTo. // WriteTo implements io.WriterTo.
@ -424,6 +437,9 @@ func (b *Reader) writeBuf(w io.Writer) (int64, error) {
// Writer implements buffering for an io.Writer object. // Writer implements buffering for an io.Writer object.
// If an error occurs writing to a Writer, no more data will be // If an error occurs writing to a Writer, no more data will be
// accepted and all subsequent writes will return the error. // accepted and all subsequent writes will return the error.
// After all data has been written, the client should call the
// Flush method to guarantee all data has been forwarded to
// the underlying io.Writer.
type Writer struct { type Writer struct {
err error err error
buf []byte buf []byte
@ -434,28 +450,41 @@ type Writer struct {
// NewWriterSize returns a new Writer whose buffer has at least the specified // NewWriterSize returns a new Writer whose buffer has at least the specified
// size. If the argument io.Writer is already a Writer with large enough // size. If the argument io.Writer is already a Writer with large enough
// size, it returns the underlying Writer. // size, it returns the underlying Writer.
func NewWriterSize(wr io.Writer, size int) *Writer { func NewWriterSize(w io.Writer, size int) *Writer {
// Is it already a Writer? // Is it already a Writer?
b, ok := wr.(*Writer) b, ok := w.(*Writer)
if ok && len(b.buf) >= size { if ok && len(b.buf) >= size {
return b return b
} }
if size <= 0 { if size <= 0 {
size = defaultBufSize size = defaultBufSize
} }
b = new(Writer) return &Writer{
b.buf = make([]byte, size) buf: make([]byte, size),
b.wr = wr wr: w,
return b }
} }
// NewWriter returns a new Writer whose buffer has the default size. // NewWriter returns a new Writer whose buffer has the default size.
func NewWriter(wr io.Writer) *Writer { func NewWriter(w io.Writer) *Writer {
return NewWriterSize(wr, defaultBufSize) return NewWriterSize(w, defaultBufSize)
}
// Reset discards any unflushed buffered data, clears any error, and
// resets b to write its output to w.
func (b *Writer) Reset(w io.Writer) {
b.err = nil
b.n = 0
b.wr = w
} }
// Flush writes any buffered data to the underlying io.Writer. // Flush writes any buffered data to the underlying io.Writer.
func (b *Writer) Flush() error { func (b *Writer) Flush() error {
err := b.flush()
return err
}
func (b *Writer) flush() error {
if b.err != nil { if b.err != nil {
return b.err return b.err
} }
@ -498,7 +527,7 @@ func (b *Writer) Write(p []byte) (nn int, err error) {
} else { } else {
n = copy(b.buf[b.n:], p) n = copy(b.buf[b.n:], p)
b.n += n b.n += n
b.Flush() b.flush()
} }
nn += n nn += n
p = p[n:] p = p[n:]
@ -517,7 +546,7 @@ func (b *Writer) WriteByte(c byte) error {
if b.err != nil { if b.err != nil {
return b.err return b.err
} }
if b.Available() <= 0 && b.Flush() != nil { if b.Available() <= 0 && b.flush() != nil {
return b.err return b.err
} }
b.buf[b.n] = c b.buf[b.n] = c
@ -540,7 +569,7 @@ func (b *Writer) WriteRune(r rune) (size int, err error) {
} }
n := b.Available() n := b.Available()
if n < utf8.UTFMax { if n < utf8.UTFMax {
if b.Flush(); b.err != nil { if b.flush(); b.err != nil {
return 0, b.err return 0, b.err
} }
n = b.Available() n = b.Available()
@ -565,7 +594,7 @@ func (b *Writer) WriteString(s string) (int, error) {
b.n += n b.n += n
nn += n nn += n
s = s[n:] s = s[n:]
b.Flush() b.flush()
} }
if b.err != nil { if b.err != nil {
return nn, b.err return nn, b.err
@ -585,23 +614,28 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
} }
var m int var m int
for { for {
if b.Available() == 0 {
if err1 := b.flush(); err1 != nil {
return n, err1
}
}
m, err = r.Read(b.buf[b.n:]) m, err = r.Read(b.buf[b.n:])
if m == 0 { if m == 0 {
break break
} }
b.n += m b.n += m
n += int64(m) n += int64(m)
if b.Available() == 0 {
if err1 := b.Flush(); err1 != nil {
return n, err1
}
}
if err != nil { if err != nil {
break break
} }
} }
if err == io.EOF { if err == io.EOF {
err = nil // If we filled the buffer exactly, flush pre-emptively.
if b.Available() == 0 {
err = b.flush()
} else {
err = nil
}
} }
return n, err return n, err
} }

View file

@ -847,6 +847,10 @@ func TestWriterReadFrom(t *testing.T) {
t.Errorf("ws[%d],rs[%d]: w.ReadFrom(r) = %d, %v, want %d, nil", wi, ri, n, err, len(input)) t.Errorf("ws[%d],rs[%d]: w.ReadFrom(r) = %d, %v, want %d, nil", wi, ri, n, err, len(input))
continue continue
} }
if err := w.Flush(); err != nil {
t.Errorf("Flush returned %v", err)
continue
}
if got, want := b.String(), string(input); got != want { if got, want := b.String(), string(input); got != want {
t.Errorf("ws[%d], rs[%d]:\ngot %q\nwant %q\n", wi, ri, got, want) t.Errorf("ws[%d], rs[%d]:\ngot %q\nwant %q\n", wi, ri, got, want)
} }
@ -1003,6 +1007,56 @@ func TestReaderClearError(t *testing.T) {
} }
} }
// Test for golang.org/issue/5947
func TestWriterReadFromWhileFull(t *testing.T) {
buf := new(bytes.Buffer)
w := NewWriterSize(buf, 10)
// Fill buffer exactly.
n, err := w.Write([]byte("0123456789"))
if n != 10 || err != nil {
t.Fatalf("Write returned (%v, %v), want (10, nil)", n, err)
}
// Use ReadFrom to read in some data.
n2, err := w.ReadFrom(strings.NewReader("abcdef"))
if n2 != 6 || err != nil {
t.Fatalf("ReadFrom returned (%v, %v), want (6, nil)", n, err)
}
}
func TestReaderReset(t *testing.T) {
r := NewReader(strings.NewReader("foo foo"))
buf := make([]byte, 3)
r.Read(buf)
if string(buf) != "foo" {
t.Errorf("buf = %q; want foo", buf)
}
r.Reset(strings.NewReader("bar bar"))
all, err := ioutil.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if string(all) != "bar bar" {
t.Errorf("ReadAll = %q; want bar bar", all)
}
}
func TestWriterReset(t *testing.T) {
var buf1, buf2 bytes.Buffer
w := NewWriter(&buf1)
w.WriteString("foo")
w.Reset(&buf2) // and not flushed
w.WriteString("bar")
w.Flush()
if buf1.String() != "" {
t.Errorf("buf1 = %q; want empty", buf1.String())
}
if buf2.String() != "bar" {
t.Errorf("buf2 = %q; want bar", buf2.String())
}
}
// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have. // An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
type onlyReader struct { type onlyReader struct {
r io.Reader r io.Reader
@ -1083,3 +1137,46 @@ func BenchmarkWriterCopyNoReadFrom(b *testing.B) {
io.Copy(dst, src) io.Copy(dst, src)
} }
} }
func BenchmarkReaderEmpty(b *testing.B) {
b.ReportAllocs()
str := strings.Repeat("x", 16<<10)
for i := 0; i < b.N; i++ {
br := NewReader(strings.NewReader(str))
n, err := io.Copy(ioutil.Discard, br)
if err != nil {
b.Fatal(err)
}
if n != int64(len(str)) {
b.Fatal("wrong length")
}
}
}
func BenchmarkWriterEmpty(b *testing.B) {
b.ReportAllocs()
str := strings.Repeat("x", 1<<10)
bs := []byte(str)
for i := 0; i < b.N; i++ {
bw := NewWriter(ioutil.Discard)
bw.Flush()
bw.WriteByte('a')
bw.Flush()
bw.WriteRune('B')
bw.Flush()
bw.Write(bs)
bw.Flush()
bw.WriteString(str)
bw.Flush()
}
}
func BenchmarkWriterFlush(b *testing.B) {
b.ReportAllocs()
bw := NewWriter(ioutil.Discard)
str := strings.Repeat("x", 50)
for i := 0; i < b.N; i++ {
bw.WriteString(str)
bw.Flush()
}
}

View file

@ -12,6 +12,14 @@ import (
"strings" "strings"
) )
func ExampleWriter() {
w := bufio.NewWriter(os.Stdout)
fmt.Fprint(w, "Hello, ")
fmt.Fprint(w, "world!")
w.Flush() // Don't forget to flush!
// Output: Hello, world!
}
// The simplest use of a Scanner, to read standard input as a set of lines. // The simplest use of a Scanner, to read standard input as a set of lines.
func ExampleScanner_lines() { func ExampleScanner_lines() {
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)

View file

@ -44,8 +44,8 @@ type Scanner struct {
// to give. The return values are the number of bytes to advance the input // to give. The return values are the number of bytes to advance the input
// and the next token to return to the user, plus an error, if any. If the // and the next token to return to the user, plus an error, if any. If the
// data does not yet hold a complete token, for instance if it has no newline // data does not yet hold a complete token, for instance if it has no newline
// while scanning lines, SplitFunc can return (0, nil) to signal the Scanner // while scanning lines, SplitFunc can return (0, nil, nil) to signal the
// to read more data into the slice and try again with a longer slice // Scanner to read more data into the slice and try again with a longer slice
// starting at the same point in the input. // starting at the same point in the input.
// //
// If the returned error is non-nil, scanning stops and the error // If the returned error is non-nil, scanning stops and the error
@ -287,7 +287,7 @@ func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
return 0, nil, nil return 0, nil, nil
} }
// isSpace returns whether the character is a Unicode white space character. // isSpace reports whether the character is a Unicode white space character.
// We avoid dependency on the unicode package, but check validity of the implementation // We avoid dependency on the unicode package, but check validity of the implementation
// in the tests. // in the tests.
func isSpace(r rune) bool { func isSpace(r rune) bool {

View file

@ -236,6 +236,19 @@ func panic(v interface{})
// panicking. // panicking.
func recover() interface{} func recover() interface{}
// The print built-in function formats its arguments in an implementation-
// specific way and writes the result to standard error.
// Print is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language.
func print(args ...Type)
// The println built-in function formats its arguments in an implementation-
// specific way and writes the result to standard error.
// Spaces are always added between arguments and a newline is appended.
// Println is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language.
func println(args ...Type)
// The error built-in interface type is the conventional interface for // The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error. // representing an error condition, with the nil value representing no error.
type error interface { type error interface {

View file

@ -11,32 +11,6 @@ import (
"unicode/utf8" "unicode/utf8"
) )
// Compare returns an integer comparing two byte slices lexicographically.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
// A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int {
m := len(a)
if m > len(b) {
m = len(b)
}
for i, ac := range a[0:m] {
bc := b[i]
switch {
case ac > bc:
return 1
case ac < bc:
return -1
}
}
switch {
case len(a) < len(b):
return -1
case len(a) > len(b):
return 1
}
return 0
}
func equalPortable(a, b []byte) bool { func equalPortable(a, b []byte) bool {
if len(a) != len(b) { if len(a) != len(b) {
return false return false
@ -103,7 +77,7 @@ func Count(s, sep []byte) int {
return count return count
} }
// Contains returns whether subslice is within b. // Contains reports whether subslice is within b.
func Contains(b, subslice []byte) bool { func Contains(b, subslice []byte) bool {
return Index(b, subslice) != -1 return Index(b, subslice) != -1
} }
@ -401,10 +375,7 @@ func Repeat(b []byte, count int) []byte {
nb := make([]byte, len(b)*count) nb := make([]byte, len(b)*count)
bp := 0 bp := 0
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
for j := 0; j < len(b); j++ { bp += copy(nb[bp:], b)
nb[bp] = b[j]
bp++
}
} }
return nb return nb
} }

View file

@ -7,10 +7,18 @@ package bytes
//go:noescape //go:noescape
// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s. // IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
func IndexByte(s []byte, c byte) int // asm_$GOARCH.s func IndexByte(s []byte, c byte) int // ../runtime/asm_$GOARCH.s
//go:noescape //go:noescape
// Equal returns a boolean reporting whether a == b. // Equal returns a boolean reporting whether a and b
// are the same length and contain the same bytes.
// A nil argument is equivalent to an empty slice. // A nil argument is equivalent to an empty slice.
func Equal(a, b []byte) bool // asm_arm.s or ../runtime/asm_{386,amd64}.s func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s
//go:noescape
// Compare returns an integer comparing two byte slices lexicographically.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
// A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int // ../runtime/noasm_arm.goc or ../runtime/asm_{386,amd64}.s

View file

@ -47,7 +47,7 @@ type BinOpTest struct {
i int i int
} }
var compareTests = []struct { var equalTests = []struct {
a, b []byte a, b []byte
i int i int
}{ }{
@ -73,12 +73,8 @@ var compareTests = []struct {
{nil, []byte("a"), -1}, {nil, []byte("a"), -1},
} }
func TestCompare(t *testing.T) { func TestEqual(t *testing.T) {
for _, tt := range compareTests { for _, tt := range compareTests {
cmp := Compare(tt.a, tt.b)
if cmp != tt.i {
t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp)
}
eql := Equal(tt.a, tt.b) eql := Equal(tt.a, tt.b)
if eql != (tt.i == 0) { if eql != (tt.i == 0) {
t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql) t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql)
@ -90,7 +86,7 @@ func TestCompare(t *testing.T) {
} }
} }
func TestEqual(t *testing.T) { func TestEqualExhaustive(t *testing.T) {
var size = 128 var size = 128
if testing.Short() { if testing.Short() {
size = 32 size = 32
@ -147,6 +143,7 @@ var indexTests = []BinOpTest{
{"", "a", -1}, {"", "a", -1},
{"", "foo", -1}, {"", "foo", -1},
{"fo", "foo", -1}, {"fo", "foo", -1},
{"foo", "baz", -1},
{"foo", "foo", 0}, {"foo", "foo", 0},
{"oofofoofooo", "f", 2}, {"oofofoofooo", "f", 2},
{"oofofoofooo", "foo", 4}, {"oofofoofooo", "foo", 4},
@ -1086,6 +1083,24 @@ func TestTitle(t *testing.T) {
} }
} }
var ToTitleTests = []TitleTest{
{"", ""},
{"a", "A"},
{" aaa aaa aaa ", " AAA AAA AAA "},
{" Aaa Aaa Aaa ", " AAA AAA AAA "},
{"123a456", "123A456"},
{"double-blind", "DOUBLE-BLIND"},
{"ÿøû", "ŸØÛ"},
}
func TestToTitle(t *testing.T) {
for _, tt := range ToTitleTests {
if s := string(ToTitle([]byte(tt.in))); s != tt.out {
t.Errorf("ToTitle(%q) = %q, want %q", tt.in, s, tt.out)
}
}
}
var EqualFoldTests = []struct { var EqualFoldTests = []struct {
s, t string s, t string
out bool out bool
@ -1114,6 +1129,37 @@ func TestEqualFold(t *testing.T) {
} }
} }
func TestBufferGrowNegative(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Fatal("Grow(-1) should have paniced")
}
}()
var b Buffer
b.Grow(-1)
}
func TestBufferTruncateNegative(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Fatal("Truncate(-1) should have paniced")
}
}()
var b Buffer
b.Truncate(-1)
}
func TestBufferTruncateOutOfRange(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Fatal("Truncate(20) should have paniced")
}
}()
var b Buffer
b.Write(make([]byte, 10))
b.Truncate(20)
}
var makeFieldsInput = func() []byte { var makeFieldsInput = func() []byte {
x := make([]byte, 1<<20) x := make([]byte, 1<<20)
// Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space. // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.

View file

@ -0,0 +1,204 @@
package bytes_test
import (
. "bytes"
"testing"
)
var compareTests = []struct {
a, b []byte
i int
}{
{[]byte(""), []byte(""), 0},
{[]byte("a"), []byte(""), 1},
{[]byte(""), []byte("a"), -1},
{[]byte("abc"), []byte("abc"), 0},
{[]byte("ab"), []byte("abc"), -1},
{[]byte("abc"), []byte("ab"), 1},
{[]byte("x"), []byte("ab"), 1},
{[]byte("ab"), []byte("x"), -1},
{[]byte("x"), []byte("a"), 1},
{[]byte("b"), []byte("x"), -1},
// test runtime·memeq's chunked implementation
{[]byte("abcdefgh"), []byte("abcdefgh"), 0},
{[]byte("abcdefghi"), []byte("abcdefghi"), 0},
{[]byte("abcdefghi"), []byte("abcdefghj"), -1},
// nil tests
{nil, nil, 0},
{[]byte(""), nil, 0},
{nil, []byte(""), 0},
{[]byte("a"), nil, 1},
{nil, []byte("a"), -1},
}
func TestCompare(t *testing.T) {
for _, tt := range compareTests {
cmp := Compare(tt.a, tt.b)
if cmp != tt.i {
t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp)
}
}
}
func TestCompareIdenticalSlice(t *testing.T) {
var b = []byte("Hello Gophers!")
if Compare(b, b) != 0 {
t.Error("b != b")
}
if Compare(b, b[:1]) != 1 {
t.Error("b > b[:1] failed")
}
}
func TestCompareBytes(t *testing.T) {
n := 128
a := make([]byte, n+1)
b := make([]byte, n+1)
for len := 0; len < 128; len++ {
// randomish but deterministic data. No 0 or 255.
for i := 0; i < len; i++ {
a[i] = byte(1 + 31*i%254)
b[i] = byte(1 + 31*i%254)
}
// data past the end is different
for i := len; i <= n; i++ {
a[i] = 8
b[i] = 9
}
cmp := Compare(a[:len], b[:len])
if cmp != 0 {
t.Errorf(`CompareIdentical(%d) = %d`, len, cmp)
}
if len > 0 {
cmp = Compare(a[:len-1], b[:len])
if cmp != -1 {
t.Errorf(`CompareAshorter(%d) = %d`, len, cmp)
}
cmp = Compare(a[:len], b[:len-1])
if cmp != 1 {
t.Errorf(`CompareBshorter(%d) = %d`, len, cmp)
}
}
for k := 0; k < len; k++ {
b[k] = a[k] - 1
cmp = Compare(a[:len], b[:len])
if cmp != 1 {
t.Errorf(`CompareAbigger(%d,%d) = %d`, len, k, cmp)
}
b[k] = a[k] + 1
cmp = Compare(a[:len], b[:len])
if cmp != -1 {
t.Errorf(`CompareBbigger(%d,%d) = %d`, len, k, cmp)
}
b[k] = a[k]
}
}
}
func BenchmarkCompareBytesEqual(b *testing.B) {
b1 := []byte("Hello Gophers!")
b2 := []byte("Hello Gophers!")
for i := 0; i < b.N; i++ {
if Compare(b1, b2) != 0 {
b.Fatal("b1 != b2")
}
}
}
func BenchmarkCompareBytesToNil(b *testing.B) {
b1 := []byte("Hello Gophers!")
var b2 []byte
for i := 0; i < b.N; i++ {
if Compare(b1, b2) != 1 {
b.Fatal("b1 > b2 failed")
}
}
}
func BenchmarkCompareBytesEmpty(b *testing.B) {
b1 := []byte("")
b2 := b1
for i := 0; i < b.N; i++ {
if Compare(b1, b2) != 0 {
b.Fatal("b1 != b2")
}
}
}
func BenchmarkCompareBytesIdentical(b *testing.B) {
b1 := []byte("Hello Gophers!")
b2 := b1
for i := 0; i < b.N; i++ {
if Compare(b1, b2) != 0 {
b.Fatal("b1 != b2")
}
}
}
func BenchmarkCompareBytesSameLength(b *testing.B) {
b1 := []byte("Hello Gophers!")
b2 := []byte("Hello, Gophers")
for i := 0; i < b.N; i++ {
if Compare(b1, b2) != -1 {
b.Fatal("b1 < b2 failed")
}
}
}
func BenchmarkCompareBytesDifferentLength(b *testing.B) {
b1 := []byte("Hello Gophers!")
b2 := []byte("Hello, Gophers!")
for i := 0; i < b.N; i++ {
if Compare(b1, b2) != -1 {
b.Fatal("b1 < b2 failed")
}
}
}
func BenchmarkCompareBytesBigUnaligned(b *testing.B) {
b.StopTimer()
b1 := make([]byte, 0, 1<<20)
for len(b1) < 1<<20 {
b1 = append(b1, "Hello Gophers!"...)
}
b2 := append([]byte("hello"), b1...)
b.StartTimer()
for i := 0; i < b.N; i++ {
if Compare(b1, b2[len("hello"):]) != 0 {
b.Fatal("b1 != b2")
}
}
b.SetBytes(int64(len(b1)))
}
func BenchmarkCompareBytesBig(b *testing.B) {
b.StopTimer()
b1 := make([]byte, 0, 1<<20)
for len(b1) < 1<<20 {
b1 = append(b1, "Hello Gophers!"...)
}
b2 := append([]byte{}, b1...)
b.StartTimer()
for i := 0; i < b.N; i++ {
if Compare(b1, b2) != 0 {
b.Fatal("b1 != b2")
}
}
b.SetBytes(int64(len(b1)))
}
func BenchmarkCompareBytesBigIdentical(b *testing.B) {
b.StopTimer()
b1 := make([]byte, 0, 1<<20)
for len(b1) < 1<<20 {
b1 = append(b1, "Hello Gophers!"...)
}
b2 := b1
b.StartTimer()
for i := 0; i < b.N; i++ {
if Compare(b1, b2) != 0 {
b.Fatal("b1 != b2")
}
}
b.SetBytes(int64(len(b1)))
}

View file

@ -41,3 +41,33 @@ Equal (struct __go_open_array a, struct __go_open_array b)
return 0; return 0;
return __builtin_memcmp (a.__values, b.__values, a.__count) == 0; return __builtin_memcmp (a.__values, b.__values, a.__count) == 0;
} }
intgo Compare (struct __go_open_array a, struct __go_open_array b)
__asm__ (GOSYM_PREFIX "bytes.Compare")
__attribute__ ((no_split_stack));
intgo
Compare (struct __go_open_array a, struct __go_open_array b)
{
intgo len;
len = a.__count;
if (len > b.__count)
len = b.__count;
if (len > 0)
{
intgo ret;
ret = __builtin_memcmp (a.__values, b.__values, len);
if (ret < 0)
return -1;
else if (ret > 0)
return 1;
}
if (a.__count < b.__count)
return -1;
else if (a.__count > b.__count)
return 1;
else
return 0;
}

View file

@ -113,6 +113,41 @@ func TestReaderWriteTo(t *testing.T) {
} }
} }
func TestReaderLen(t *testing.T) {
const data = "hello world"
r := NewReader([]byte(data))
if got, want := r.Len(), 11; got != want {
t.Errorf("r.Len(): got %d, want %d", got, want)
}
if n, err := r.Read(make([]byte, 10)); err != nil || n != 10 {
t.Errorf("Read failed: read %d %v", n, err)
}
if got, want := r.Len(), 1; got != want {
t.Errorf("r.Len(): got %d, want %d", got, want)
}
if n, err := r.Read(make([]byte, 1)); err != nil || n != 1 {
t.Errorf("Read failed: read %d %v", n, err)
}
if got, want := r.Len(), 0; got != want {
t.Errorf("r.Len(): got %d, want %d", got, want)
}
}
func TestReaderDoubleUnreadRune(t *testing.T) {
buf := NewBuffer([]byte("groucho"))
if _, _, err := buf.ReadRune(); err != nil {
// should not happen
t.Fatal(err)
}
if err := buf.UnreadByte(); err != nil {
// should not happen
t.Fatal(err)
}
if err := buf.UnreadByte(); err == nil {
t.Fatal("UnreadByte: expected error, got nil")
}
}
// verify that copying from an empty reader always has the same results, // verify that copying from an empty reader always has the same results,
// regardless of the presence of a WriteTo method. // regardless of the presence of a WriteTo method.
func TestReaderCopyNothing(t *testing.T) { func TestReaderCopyNothing(t *testing.T) {

View file

@ -77,6 +77,14 @@ func (br *bitReader) ReadBit() bool {
return n != 0 return n != 0
} }
func (br *bitReader) TryReadBit() (bit byte, ok bool) {
if br.bits > 0 {
br.bits--
return byte(br.n>>br.bits) & 1, true
}
return 0, false
}
func (br *bitReader) Err() error { func (br *bitReader) Err() error {
return br.err return br.err
} }

View file

@ -22,14 +22,17 @@ func (s StructuralError) Error() string {
// A reader decompresses bzip2 compressed data. // A reader decompresses bzip2 compressed data.
type reader struct { type reader struct {
br bitReader br bitReader
setupDone bool // true if we have parsed the bzip2 header. fileCRC uint32
blockSize int // blockSize in bytes, i.e. 900 * 1024. blockCRC uint32
eof bool wantBlockCRC uint32
buf []byte // stores Burrows-Wheeler transformed data. setupDone bool // true if we have parsed the bzip2 header.
c [256]uint // the `C' array for the inverse BWT. blockSize int // blockSize in bytes, i.e. 900 * 1024.
tt []uint32 // mirrors the `tt' array in the bzip2 source and contains the P array in the upper 24 bits. eof bool
tPos uint32 // Index of the next output byte in tt. buf []byte // stores Burrows-Wheeler transformed data.
c [256]uint // the `C' array for the inverse BWT.
tt []uint32 // mirrors the `tt' array in the bzip2 source and contains the P array in the upper 24 bits.
tPos uint32 // Index of the next output byte in tt.
preRLE []uint32 // contains the RLE data still to be processed. preRLE []uint32 // contains the RLE data still to be processed.
preRLEUsed int // number of entries of preRLE used. preRLEUsed int // number of entries of preRLE used.
@ -50,12 +53,14 @@ const bzip2BlockMagic = 0x314159265359
const bzip2FinalMagic = 0x177245385090 const bzip2FinalMagic = 0x177245385090
// setup parses the bzip2 header. // setup parses the bzip2 header.
func (bz2 *reader) setup() error { func (bz2 *reader) setup(needMagic bool) error {
br := &bz2.br br := &bz2.br
magic := br.ReadBits(16) if needMagic {
if magic != bzip2FileMagic { magic := br.ReadBits(16)
return StructuralError("bad magic value") if magic != bzip2FileMagic {
return StructuralError("bad magic value")
}
} }
t := br.ReadBits(8) t := br.ReadBits(8)
@ -68,8 +73,11 @@ func (bz2 *reader) setup() error {
return StructuralError("invalid compression level") return StructuralError("invalid compression level")
} }
bz2.fileCRC = 0
bz2.blockSize = 100 * 1024 * (int(level) - '0') bz2.blockSize = 100 * 1024 * (int(level) - '0')
bz2.tt = make([]uint32, bz2.blockSize) if bz2.blockSize > len(bz2.tt) {
bz2.tt = make([]uint32, bz2.blockSize)
}
return nil return nil
} }
@ -79,7 +87,7 @@ func (bz2 *reader) Read(buf []byte) (n int, err error) {
} }
if !bz2.setupDone { if !bz2.setupDone {
err = bz2.setup() err = bz2.setup(true)
brErr := bz2.br.Err() brErr := bz2.br.Err()
if brErr != nil { if brErr != nil {
err = brErr err = brErr
@ -98,14 +106,14 @@ func (bz2 *reader) Read(buf []byte) (n int, err error) {
return return
} }
func (bz2 *reader) read(buf []byte) (n int, err error) { func (bz2 *reader) readFromBlock(buf []byte) int {
// bzip2 is a block based compressor, except that it has a run-length // bzip2 is a block based compressor, except that it has a run-length
// preprocessing step. The block based nature means that we can // preprocessing step. The block based nature means that we can
// preallocate fixed-size buffers and reuse them. However, the RLE // preallocate fixed-size buffers and reuse them. However, the RLE
// preprocessing would require allocating huge buffers to store the // preprocessing would require allocating huge buffers to store the
// maximum expansion. Thus we process blocks all at once, except for // maximum expansion. Thus we process blocks all at once, except for
// the RLE which we decompress as required. // the RLE which we decompress as required.
n := 0
for (bz2.repeats > 0 || bz2.preRLEUsed < len(bz2.preRLE)) && n < len(buf) { for (bz2.repeats > 0 || bz2.preRLEUsed < len(bz2.preRLE)) && n < len(buf) {
// We have RLE data pending. // We have RLE data pending.
@ -148,34 +156,87 @@ func (bz2 *reader) read(buf []byte) (n int, err error) {
n++ n++
} }
if n > 0 { return n
return }
func (bz2 *reader) read(buf []byte) (int, error) {
for {
n := bz2.readFromBlock(buf)
if n > 0 {
bz2.blockCRC = updateCRC(bz2.blockCRC, buf[:n])
return n, nil
}
// End of block. Check CRC.
if bz2.blockCRC != bz2.wantBlockCRC {
bz2.br.err = StructuralError("block checksum mismatch")
return 0, bz2.br.err
}
// Find next block.
br := &bz2.br
switch br.ReadBits64(48) {
default:
return 0, StructuralError("bad magic value found")
case bzip2BlockMagic:
// Start of block.
err := bz2.readBlock()
if err != nil {
return 0, err
}
case bzip2FinalMagic:
// Check end-of-file CRC.
wantFileCRC := uint32(br.ReadBits64(32))
if br.err != nil {
return 0, br.err
}
if bz2.fileCRC != wantFileCRC {
br.err = StructuralError("file checksum mismatch")
return 0, br.err
}
// Skip ahead to byte boundary.
// Is there a file concatenated to this one?
// It would start with BZ.
if br.bits%8 != 0 {
br.ReadBits(br.bits % 8)
}
b, err := br.r.ReadByte()
if err == io.EOF {
br.err = io.EOF
bz2.eof = true
return 0, io.EOF
}
if err != nil {
br.err = err
return 0, err
}
z, err := br.r.ReadByte()
if err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
br.err = err
return 0, err
}
if b != 'B' || z != 'Z' {
return 0, StructuralError("bad magic value in continuation file")
}
if err := bz2.setup(false); err != nil {
return 0, err
}
}
} }
// No RLE data is pending so we need to read a block.
br := &bz2.br
magic := br.ReadBits64(48)
if magic == bzip2FinalMagic {
br.ReadBits64(32) // ignored CRC
bz2.eof = true
return 0, io.EOF
} else if magic != bzip2BlockMagic {
return 0, StructuralError("bad magic value found")
}
err = bz2.readBlock()
if err != nil {
return 0, err
}
return bz2.read(buf)
} }
// readBlock reads a bzip2 block. The magic number should already have been consumed. // readBlock reads a bzip2 block. The magic number should already have been consumed.
func (bz2 *reader) readBlock() (err error) { func (bz2 *reader) readBlock() (err error) {
br := &bz2.br br := &bz2.br
br.ReadBits64(32) // skip checksum. TODO: check it if we can figure out what it is. bz2.wantBlockCRC = uint32(br.ReadBits64(32)) // skip checksum. TODO: check it if we can figure out what it is.
bz2.blockCRC = 0
bz2.fileCRC = (bz2.fileCRC<<1 | bz2.fileCRC>>31) ^ bz2.wantBlockCRC
randomized := br.ReadBits(1) randomized := br.ReadBits(1)
if randomized != 0 { if randomized != 0 {
return StructuralError("deprecated randomized files") return StructuralError("deprecated randomized files")
@ -316,6 +377,9 @@ func (bz2 *reader) readBlock() (err error) {
if repeat > 0 { if repeat > 0 {
// We have decoded a complete run-length so we need to // We have decoded a complete run-length so we need to
// replicate the last output symbol. // replicate the last output symbol.
if repeat > bz2.blockSize-bufIndex {
return StructuralError("repeats past end of block")
}
for i := 0; i < repeat; i++ { for i := 0; i < repeat; i++ {
b := byte(mtf.First()) b := byte(mtf.First())
bz2.tt[bufIndex] = uint32(b) bz2.tt[bufIndex] = uint32(b)
@ -339,6 +403,9 @@ func (bz2 *reader) readBlock() (err error) {
// doesn't need to be encoded and we have |v-1| in the next // doesn't need to be encoded and we have |v-1| in the next
// line. // line.
b := byte(mtf.Decode(int(v - 1))) b := byte(mtf.Decode(int(v - 1)))
if bufIndex >= bz2.blockSize {
return StructuralError("data exceeds block size")
}
bz2.tt[bufIndex] = uint32(b) bz2.tt[bufIndex] = uint32(b)
bz2.c[b]++ bz2.c[b]++
bufIndex++ bufIndex++
@ -385,3 +452,33 @@ func inverseBWT(tt []uint32, origPtr uint, c []uint) uint32 {
return tt[origPtr] >> 8 return tt[origPtr] >> 8
} }
// This is a standard CRC32 like in hash/crc32 except that all the shifts are reversed,
// causing the bits in the input to be processed in the reverse of the usual order.
var crctab [256]uint32
func init() {
const poly = 0x04C11DB7
for i := range crctab {
crc := uint32(i) << 24
for j := 0; j < 8; j++ {
if crc&0x80000000 != 0 {
crc = (crc << 1) ^ poly
} else {
crc <<= 1
}
}
crctab[i] = crc
}
}
// updateCRC updates the crc value to incorporate the data in b.
// The initial value is 0.
func updateCRC(val uint32, b []byte) uint32 {
crc := ^val
for _, v := range b {
crc = crctab[byte(crc>>24)^v] ^ (crc << 8)
}
return ^crc
}

View file

@ -6,6 +6,7 @@ package bzip2
import ( import (
"bytes" "bytes"
"encoding/base64"
"encoding/hex" "encoding/hex"
"io" "io"
"io/ioutil" "io/ioutil"
@ -62,6 +63,19 @@ func TestHelloWorldBZ2(t *testing.T) {
} }
} }
func TestConcat(t *testing.T) {
out, err := decompressHex(helloWorldBZ2Hex + helloWorldBZ2Hex)
if err != nil {
t.Errorf("error from Read: %s", err)
return
}
hello2 := bytes.Repeat(helloWorld, 2)
if !bytes.Equal(hello2, out) {
t.Errorf("got %x, want %x", out, hello2)
}
}
func testZeros(t *testing.T, inHex string, n int) { func testZeros(t *testing.T, inHex string, n int) {
out, err := decompressHex(inHex) out, err := decompressHex(inHex)
if err != nil { if err != nil {
@ -155,3 +169,195 @@ const rand2Hex = "92d5652616ac444a4a04af1a8a3964aca0450d43d6cf233bd03233f4ba92f8
const rand3BZ2Hex = "425a68393141592653593be669d00000327ffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffc002b3b2b1b6e2bae400004c00132300004c0d268c004c08c0130026001a008683234c0684c34008c230261a04c0260064d07a8d00034000d27a1268c9931a8d327a3427a41faa69ea0da264c1a34219326869b51b49a6469a3268c689fa53269a62794687a9a68f5189994c9e487a8f534fd49a3d34043629e8c93d04da4f4648d30d4f44d3234c4d3023d0840680984d309934c234d3131a000640984f536a6132601300130130c8d00d04d1841ea7a8d31a02609b40023460010c01a34d4c1a0d04d3069306810034d0d0d4c0046130d034d0131a9a64d321804c68003400098344c13000991808c0001a00000000098004d3d4da4604c47a13012140aadf8d673c922c607ef6212a8c0403adea4b28aee578900e653b9cdeb8d11e6b838815f3ebaad5a01c5408d84a332170aff8734d4e06612d3c2889f31925fb89e33561f5100ae89b1f7047102e729373d3667e58d73aaa80fa7be368a1cc2dadd81d81ec8e1b504bd772ca31d03649269b01ceddaca07bf3d4eba24de141be3f86f93601e03714c0f64654671684f9f9528626fd4e1b76753dc0c54b842486b8d59d8ab314e86ca818e7a1f079463cbbd70d9b79b283c7edc419406311022e4be98c2c1374df9cdde2d008ce1d00e5f06ad1024baf555631f70831fc1023034e62be7c4bcb648caf276963ffa20e96bb50377fe1c113da0db4625b50741c35a058edb009c6ee5dbf93b8a6b060eec568180e8db791b82aab96cbf4326ca98361461379425ba8dcc347be670bdba7641883e5526ae3d833f6e9cb9bac9557747c79e206151072f7f0071dff3880411846f66bf4075c7462f302b53cb3400a74cf35652ad5641ed33572fd54e7ed7f85f58a0acba89327e7c6be5c58cb71528b99df2431f1d0358f8d28d81d95292da631fb06701decabb205fac59ff0fb1df536afc681eece6ea658c4d9eaa45f1342aa1ff70bdaff2ddaf25ec88c22f12829a0553db1ec2505554cb17d7b282e213a5a2aa30431ded2bce665bb199d023840832fedb2c0c350a27291407ff77440792872137df281592e82076a05c64c345ffb058c64f7f7c207ef78420b7010520610f17e302cc4dfcfaef72a0ed091aab4b541eb0531bbe941ca2f792bf7b31ca6162882b68054a8470115bc2c19f2df2023f7800432b39b04d3a304e8085ba3f1f0ca5b1ba4d38d339e6084de979cdea6d0e244c6c9fa0366bd890621e3d30846f5e8497e21597b8f29bbf52c961a485dfbea647600da0fc1f25ce4d203a8352ece310c39073525044e7ac46acf2ed9120bae1b4f6f02364abfe343f80b290983160c103557af1c68416480d024cc31b6c06cfec011456f1e95c420a12b48b1c3fe220c2879a982fb099948ac440db844b9a112a5188c7783fd3b19593290785f908d95c9db4b280bafe89c1313aeec24772046d9bc089645f0d182a21184e143823c5f52de50e5d7e98d3d7ab56f5413bbccd1415c9bcff707def475b643fb7f29842582104d4cc1dbaaca8f10a2f44273c339e0984f2b1e06ab2f0771db01fafa8142298345f3196f23e5847bda024034b6f59b11c29e981c881456e40d211929fd4f766200258aad8212016322bd5c605790dcfdf1bd2a93d99c9b8f498722d311d7eae7ff420496a31804c55f4759a7b13aaaf5f7ce006c3a8a998897d5e0a504398c2b627852545baf440798bcc5cc049357cf3f17d9771e4528a1af3d77dc794a11346e1bdf5efe37a405b127b4c43b616d61fbc5dc914e14240ef99a7400" const rand3BZ2Hex = "425a68393141592653593be669d00000327ffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffc002b3b2b1b6e2bae400004c00132300004c0d268c004c08c0130026001a008683234c0684c34008c230261a04c0260064d07a8d00034000d27a1268c9931a8d327a3427a41faa69ea0da264c1a34219326869b51b49a6469a3268c689fa53269a62794687a9a68f5189994c9e487a8f534fd49a3d34043629e8c93d04da4f4648d30d4f44d3234c4d3023d0840680984d309934c234d3131a000640984f536a6132601300130130c8d00d04d1841ea7a8d31a02609b40023460010c01a34d4c1a0d04d3069306810034d0d0d4c0046130d034d0131a9a64d321804c68003400098344c13000991808c0001a00000000098004d3d4da4604c47a13012140aadf8d673c922c607ef6212a8c0403adea4b28aee578900e653b9cdeb8d11e6b838815f3ebaad5a01c5408d84a332170aff8734d4e06612d3c2889f31925fb89e33561f5100ae89b1f7047102e729373d3667e58d73aaa80fa7be368a1cc2dadd81d81ec8e1b504bd772ca31d03649269b01ceddaca07bf3d4eba24de141be3f86f93601e03714c0f64654671684f9f9528626fd4e1b76753dc0c54b842486b8d59d8ab314e86ca818e7a1f079463cbbd70d9b79b283c7edc419406311022e4be98c2c1374df9cdde2d008ce1d00e5f06ad1024baf555631f70831fc1023034e62be7c4bcb648caf276963ffa20e96bb50377fe1c113da0db4625b50741c35a058edb009c6ee5dbf93b8a6b060eec568180e8db791b82aab96cbf4326ca98361461379425ba8dcc347be670bdba7641883e5526ae3d833f6e9cb9bac9557747c79e206151072f7f0071dff3880411846f66bf4075c7462f302b53cb3400a74cf35652ad5641ed33572fd54e7ed7f85f58a0acba89327e7c6be5c58cb71528b99df2431f1d0358f8d28d81d95292da631fb06701decabb205fac59ff0fb1df536afc681eece6ea658c4d9eaa45f1342aa1ff70bdaff2ddaf25ec88c22f12829a0553db1ec2505554cb17d7b282e213a5a2aa30431ded2bce665bb199d023840832fedb2c0c350a27291407ff77440792872137df281592e82076a05c64c345ffb058c64f7f7c207ef78420b7010520610f17e302cc4dfcfaef72a0ed091aab4b541eb0531bbe941ca2f792bf7b31ca6162882b68054a8470115bc2c19f2df2023f7800432b39b04d3a304e8085ba3f1f0ca5b1ba4d38d339e6084de979cdea6d0e244c6c9fa0366bd890621e3d30846f5e8497e21597b8f29bbf52c961a485dfbea647600da0fc1f25ce4d203a8352ece310c39073525044e7ac46acf2ed9120bae1b4f6f02364abfe343f80b290983160c103557af1c68416480d024cc31b6c06cfec011456f1e95c420a12b48b1c3fe220c2879a982fb099948ac440db844b9a112a5188c7783fd3b19593290785f908d95c9db4b280bafe89c1313aeec24772046d9bc089645f0d182a21184e143823c5f52de50e5d7e98d3d7ab56f5413bbccd1415c9bcff707def475b643fb7f29842582104d4cc1dbaaca8f10a2f44273c339e0984f2b1e06ab2f0771db01fafa8142298345f3196f23e5847bda024034b6f59b11c29e981c881456e40d211929fd4f766200258aad8212016322bd5c605790dcfdf1bd2a93d99c9b8f498722d311d7eae7ff420496a31804c55f4759a7b13aaaf5f7ce006c3a8a998897d5e0a504398c2b627852545baf440798bcc5cc049357cf3f17d9771e4528a1af3d77dc794a11346e1bdf5efe37a405b127b4c43b616d61fbc5dc914e14240ef99a7400"
const rand3Hex = "1744b384d68c042371244e13500d4bfb98c6244e3d71a5b700224420b59c593553f33bd786e3d0ce31626f511bc985f59d1a88aa38ba8ad6218d306abee60dd9172540232b95be1af146c69e72e5fde667a090dc3f93bdc5c5af0ab80acdbaa7a505f628c59dc0247b31a439cacf5010a94376d71521df08c178b02fb96fdb1809144ea38c68536187c53201fea8631fb0a880b4451ccdca7cc61f6aafca21cc7449d920599db61789ac3b1e164b3390124f95022aeea39ccca3ec1053f4fa10de2978e2861ea58e477085c2220021a0927aa94c5d0006b5055abba340e4f9eba22e969978dfd18e278a8b89d877328ae34268bc0174cfe211954c0036f078025217d1269fac1932a03b05a0b616012271bbe1fb554171c7a59b196d8a4479f45a77931b5d97aaf6c0c673cbe597b79b96e2a0c1eae2e66e46ccc8c85798e23ffe972ebdaa3f6caea243c004e60321eb47cd79137d78fd0613be606feacc5b3637bdc96a89c13746db8cad886f3ccf912b2178c823bcac395f06d28080269bdca2debf3419c66c690fd1adcfbd53e32e79443d7a42511a84cb22ca94fffad9149275a075b2f8ae0b021dcde9bf62b102db920733b897560518b06e1ad7f4b03458493ddaa7f4fa2c1609f7a1735aeeb1b3e2cea3ab45fc376323cc91873b7e9c90d07c192e38d3f5dfc9bfab1fd821c854da9e607ea596c391c7ec4161c6c4493929a8176badaa5a5af7211c623f29643a937677d3df0da9266181b7c4da5dd40376db677fe8f4a1dc456adf6f33c1e37cec471dd318c2647644fe52f93707a77da7d1702380a80e14cc0fdce7bf2eed48a529090bae0388ee277ce6c7018c5fb00b88362554362205c641f0d0fab94fd5b8357b5ff08b207fee023709bc126ec90cfb17c006754638f8186aaeb1265e80be0c1189ec07d01d5f6f96cb9ce82744147d18490de7dc72862f42f024a16968891a356f5e7e0e695d8c933ba5b5e43ad4c4ade5399bc2cae9bb6189b7870d7f22956194d277f28b10e01c10c6ffe3e065f7e2d6d056aa790db5649ca84dc64c35566c0af1b68c32b5b7874aaa66467afa44f40e9a0846a07ae75360a641dd2acc69d93219b2891f190621511e62a27f5e4fbe641ece1fa234fc7e9a74f48d2a760d82160d9540f649256b169d1fed6fbefdc491126530f3cbad7913e19fbd7aa53b1e243fbf28d5f38c10ebd77c8b986775975cc1d619efb27cdcd733fa1ca36cffe9c0a33cc9f02463c91a886601fd349efee85ef1462065ef9bd2c8f533220ad93138b8382d5938103ab25b2d9af8ae106e1211eb9b18793fba033900c809c02cd6d17e2f3e6fc84dae873411f8e87c3f0a8f1765b7825d185ce3730f299c3028d4a62da9ee95c2b870fb70c79370d485f9d5d9acb78926d20444033d960524d2776dc31988ec7c0dbf23b9905d" const rand3Hex = "1744b384d68c042371244e13500d4bfb98c6244e3d71a5b700224420b59c593553f33bd786e3d0ce31626f511bc985f59d1a88aa38ba8ad6218d306abee60dd9172540232b95be1af146c69e72e5fde667a090dc3f93bdc5c5af0ab80acdbaa7a505f628c59dc0247b31a439cacf5010a94376d71521df08c178b02fb96fdb1809144ea38c68536187c53201fea8631fb0a880b4451ccdca7cc61f6aafca21cc7449d920599db61789ac3b1e164b3390124f95022aeea39ccca3ec1053f4fa10de2978e2861ea58e477085c2220021a0927aa94c5d0006b5055abba340e4f9eba22e969978dfd18e278a8b89d877328ae34268bc0174cfe211954c0036f078025217d1269fac1932a03b05a0b616012271bbe1fb554171c7a59b196d8a4479f45a77931b5d97aaf6c0c673cbe597b79b96e2a0c1eae2e66e46ccc8c85798e23ffe972ebdaa3f6caea243c004e60321eb47cd79137d78fd0613be606feacc5b3637bdc96a89c13746db8cad886f3ccf912b2178c823bcac395f06d28080269bdca2debf3419c66c690fd1adcfbd53e32e79443d7a42511a84cb22ca94fffad9149275a075b2f8ae0b021dcde9bf62b102db920733b897560518b06e1ad7f4b03458493ddaa7f4fa2c1609f7a1735aeeb1b3e2cea3ab45fc376323cc91873b7e9c90d07c192e38d3f5dfc9bfab1fd821c854da9e607ea596c391c7ec4161c6c4493929a8176badaa5a5af7211c623f29643a937677d3df0da9266181b7c4da5dd40376db677fe8f4a1dc456adf6f33c1e37cec471dd318c2647644fe52f93707a77da7d1702380a80e14cc0fdce7bf2eed48a529090bae0388ee277ce6c7018c5fb00b88362554362205c641f0d0fab94fd5b8357b5ff08b207fee023709bc126ec90cfb17c006754638f8186aaeb1265e80be0c1189ec07d01d5f6f96cb9ce82744147d18490de7dc72862f42f024a16968891a356f5e7e0e695d8c933ba5b5e43ad4c4ade5399bc2cae9bb6189b7870d7f22956194d277f28b10e01c10c6ffe3e065f7e2d6d056aa790db5649ca84dc64c35566c0af1b68c32b5b7874aaa66467afa44f40e9a0846a07ae75360a641dd2acc69d93219b2891f190621511e62a27f5e4fbe641ece1fa234fc7e9a74f48d2a760d82160d9540f649256b169d1fed6fbefdc491126530f3cbad7913e19fbd7aa53b1e243fbf28d5f38c10ebd77c8b986775975cc1d619efb27cdcd733fa1ca36cffe9c0a33cc9f02463c91a886601fd349efee85ef1462065ef9bd2c8f533220ad93138b8382d5938103ab25b2d9af8ae106e1211eb9b18793fba033900c809c02cd6d17e2f3e6fc84dae873411f8e87c3f0a8f1765b7825d185ce3730f299c3028d4a62da9ee95c2b870fb70c79370d485f9d5d9acb78926d20444033d960524d2776dc31988ec7c0dbf23b9905d"
const (
digits = iota
twain
)
var testfiles = []string{
// Digits is the digits of the irrational number e. Its decimal representation
// does not repeat, but there are only 10 posible digits, so it should be
// reasonably compressible.
digits: "testdata/e.txt.bz2",
// Twain is Project Gutenberg's edition of Mark Twain's classic English novel.
twain: "testdata/Mark.Twain-Tom.Sawyer.txt.bz2",
}
func benchmarkDecode(b *testing.B, testfile int) {
compressed, err := ioutil.ReadFile(testfiles[testfile])
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(len(compressed)))
for i := 0; i < b.N; i++ {
r := bytes.NewBuffer(compressed)
io.Copy(ioutil.Discard, NewReader(r))
}
}
func BenchmarkDecodeDigits(b *testing.B) { benchmarkDecode(b, digits) }
func BenchmarkDecodeTwain(b *testing.B) { benchmarkDecode(b, twain) }
func TestBufferOverrun(t *testing.T) {
// Tests https://code.google.com/p/go/issues/detail?id=5747.
buffer := bytes.NewBuffer([]byte(bufferOverrunBase64))
decoder := base64.NewDecoder(base64.StdEncoding, buffer)
decompressor := NewReader(decoder)
// This shouldn't panic.
ioutil.ReadAll(decompressor)
}
var bufferOverrunBase64 string = `
QlpoNTFBWSZTWTzyiGcACMP/////////////////////////////////3/7f3///
////4N/fCZODak2Xo44GIHZgkGzDRbFAuwAAKoFV7T6AO6qwA6APb6s2rOoAkAAD
oACUoDtndh0iQAPkAAAAaPWihQoCgr5t97Obju21ChQB0NBm3RbA7apXrRoBooAA
AhA+IAHWl2Us3O7t9yieb3udvd76+4+fd33nd3HO1bVvfcGRne6+3vfPvfc++995
w7k973eJhasLVec970tzDNXdX28LoPXZ3H3K9z0s5ufWAfes49d5594c3dUYtI+2
+h1dvtpRa+uvrVEAG9bl893RVEN7cWvroSqWjPMGgAQi7Gq8TJSgKKdjKFBIB9Ae
LqWxleu715eXe7ml9e5098Z6G1vr7t1QZ6ot76YzPd3j7333t2ql2Chm7XrA9ICQ
VF77z3rVBWqkSXtlfb099hyezAr6USbGpICTSCFAaqHrKo+tUnm32rpE4Ue+t2mj
bKUeipEqwc93EdhhTwmQpOhhesC9iqDSPNTWYNSnUtBdm1nsA0nqqNd7OWwDXtFL
ONmmA6Ubke26I9UblvWIPR5VOWOnctai443URunnDy77uVC59OfRvezlDu33Z7Ly
3NNuuHW63088xu3t3NHZhkZbG7tXRlj00qOtbaXTJUUdspTbABR9R6EUwQAEAAAA
EMEwRpoAAAABMmhoAAjBNNAaCMhponpoGpgJpk9TEyp6niGKZkAaAEfqMQ09U80p
+pMGSCKngIAAAAgAAg0AAJhGgABGCEaaTyTKeNI1PE0wkj01GajMSNPSZGnqbU9T
anlPUNAHqGQ0DQAMg9TamgAAYRU/IAAICAmjQJgjQBMEwp5DTSaaYmhTeqfplPID
U1T9TynoU82pT1NPU/VP0j1NHqRpk9TTR7SnqaNNGmmQAaAD1Aeo0PSAAAAaaBiK
eBAQBGgIABGQA0AmBNNBoaAgaJmpglPEyYap6npiTT0agGjJjUaaDTQAAAAAAM1A
9QAaAAAADU8iEAQAEyAJk0NNNJgIZTJ5E00YSemiaZNGm1MpGNJ+lPU9qm9U2RDM
oY0EzJB6h6nqDID1NMBDDRpo1AGNAjCMmhkMgaYSJIgAAAQyAAEyBoATECCNhTT0
U/IZAmCM1DSTxkzUE8p6NDaGiZGJqntTFHvUyU9qPQp7Kn5GgKNPU9QAGg9QAAA3
wz0Pk/g/m/m9P9H4vxv2+dH3gCS8nhbbbbbYxtgNsBsG0m2MbG0NNtsbYNsaY0wb
bBibGmm22mxptNpsaGNDTY02JsG0MY0xg2MaYNNDbGwG0L5vsK/F9DO+EAA447Kq
p7Wdf6Y+5c20T7DfHyMXIzRKrZexw72uiQI+y55vOe52xpqbCLC2uR20JdER7Zvr
7ufuKb6zhiBxLuj0eA27v8RpMLucw9Ohwcizi2wrpt+yU1FdpM7ZYPcwS3XTef+A
Wzjxwhdrgw3aH1LeC1eZW900x8V9Nv4hTPXp4l067P/4ANVZFF/imOe/d5bdueam
/DFFokQWnFaU+ZqLBCM+d0PialJQWnLqRQZk/KhfbbYc2pCUTgffcSYbrCM1N+8l
HU6gSz+h2GJXs+tbrNviL83M97X0vcTn/F82P8wen8/3/h3sHY+sf9CSej9ThYTV
3lQ+FUHpfpGD4kv7dYMV995dpDX/y3xR8FoXx1bjUxBTNxuutwQ/h/Eedn9wpn6w
E3+ND8YhN1HSriIxRE/6uFyMv6/oC6Elarw3aHMMqHJkGiiz6tejmvnYLQa+Qm6G
deZ7jXTZV6NlpocgDnRdimS06bTYSkvPAL/xoWNLkX6N6VljU0dfKSBmm2uZE/xu
sutQ1EdP7GdjhglIq4xlOFUFEQpmX+xx7R8y6c0GSAaqusOjNZwxZRudOvmXm1tZ
T+YnbeB2ir9eiHNrtJNSLD/J/WDyuQpwBUtLKo0krccY/wIILP7f86teb9Z/9oyz
OX05qEWbObfhpRw+9+rCvp/35ML8KX3aHaI0n+tudbFRsV5FLW+Oa8ruLN4peyVL
DWjTHrXNthq/s7zAJYMeFJZkZt5mT9rfpH+5g3nc+piOSZ+J5nHtOnKI7Ff8Xl+j
0t76XTNucCHQ6whav1OHdF53TY5wuv5OzvrdnxoId8fTyUvERr0ERINu/8XxZZ5f
B5/kTZ8bBO0wv54Jp+ED/GQI8lZHzIQCP3vfQhwnCTj9TvITic7P4mYLDbH3fyzR
i+6EajCcpXLWSGf+ZXkOrWspDWDhXtEKas0v3UqWksqgY1rTj45krX4KihN+daXs
pZl5WPlta5p06CX6Xm2SfzqkMw12/3ix1bpnnZ+kFeBNX7A+E9zzG6OZaN78GOpl
9Ht/eZn9PqWdav852zr0zqkDK2H5IjdvNah+b1YVGdQGzwR4Nw+f13yEKnV+y66W
djfq7zWp7m5w+hzfv+Ly8O7oet5Vvd8/wQvO7qzOZ2vjf9X8Tj8PnMb/nc/nKqRR
+ml4UEhOOwfCeJEEI109CMYSh91iAJqPjMyH6KjrPD7W25llZVcREYNCTg6htbQt
M38wYoquCWP6tdKYlVIv14xTNUeUf4El/FunCf6csZkmv+9tfWx7t59wuKIa3saU
tZs9M+3HFOZtz3OLg/Unoaj9BYazYqA78xBU9tZzrtmF/rQL9CGJt90o/oYnSfcS
SL3haaw351LXWQ1XOsv1SmH3v6ymuxEpPPnEDmBELaTYsvvMIWJsmPZFFww++Kd7
s/Jo0JFeUU7uNtI+gVosAIpVVuWfI/9tOIycz7I5Z7zjV+NR2OuZbYtW5F08KX4o
2k/xuJIchcNFPtxPfw9dkDgscRbMckyFMrzuZ3IvrcGzk0J6iI5ytrv37bGpAXMz
WK9mMMPebepNevmLjjo/QWoM968Sjv7ldlPS5AinHcXwsFv6dmmh8lJt7UOJWoKu
lMD1cB2ksIGpMdv8iuqR42Rn/kn+17BhhUZcwDBaUXVdX6bKW7fxlUYbq+mlqIcf
a9v8HF87M9ANbi9bq9onf9TD7nQ6Xf6vZci8TBPX+/GI0He6j31fTVQYW+NsQxvO
J8xrx+e58CCLQNjxeIyPt+F+qk/QMiXw+LyxGVkV/XcGQT9X03jSDP6beJ5QG1JW
9Q3qLv/YixWI7gPV9Mrhf2oRYTc/9KLFRhkE3SjKOTKuSSBKQ24fI+hEznamH71D
66Hwez8/0et7AtTv9zvamv2OD5He6fMV4k+ePl6+qPfO5CdHtK+eCDZL5+4f5yrl
gTcRFiq8fXbc5IaI5fbbc1KMM/2T0Mr7+Hwaco6FtXm0fmhCgTZRqY4pKiEIfmaz
QwHNOOCrtMJ2VwsyMumt7xsOolGnizRev6lILH43qPcczQM7Gc5zRin80YvFt1Qm
h/57Z0auR2h0fuX50MBO4XQ+26y5l6v4j902R66c0j3z2KHstKQ04J/h6LbuNQE4
D6cu/lyfK69DxxX8wb8XaQkMUcJdo1LzqUGDAb3Kfn/A3P/JYc99MO9qv67+SxWb
wYTyqKdWTd+1KbR/Rcn0Io5zI/QquX7FA1bxfMytjQ/X+l0fh0Pf+Hx97meH4fQL
7/T8/sdTm9Tn8nELvedyhydLlPPTScINdXyLIq9wgIJr4fWPbp9ZhFh/56fdSgOG
HDXg+gkXsN2Rddr4HQ5P3u+RhLzmSjhzoqY5EsPC4QvRlX9JXjB84rPV5USR66qa
/kjw4156GJnzoXtydKJE53t6PHfZWO+3ujsfI6iAdshc7OFzGXiZB9PtItKodhYq
nABkTKdcpu4+TOpf9h5piX5slsaBjkeTnj/Ba02ilboQfcDVigxrYn/iTH5ySWUW
/lHtg78s5UZM8sErwhNe3N3w+6ZOMnU+5i86/xFNtqZfDdXTGy1H3PzGbdtZXYT+
Ixx2vpwBYzbPVYHxKosM5rPiVmcTllI9nuoSfeh9ib4foFWauOpvdmhBDqpTpKTX
u8EO2l2Z195G2RIV7TlKSxGWjR5sl/nALu1uzBeLd9zpSujzMTd1uTX9Qk/Q1S+r
vaW6bm8qqPO4jb6Wx6XIkm321nrIF6Ae25d1+Dpv/P5G4NoLd2j6/EtENC3FeR5z
oo7bA+tI8yEQRhiF0z1FlJXLD5ZbhNNWQm/j/IbzRfh8JtOFZU7ruShLvHXysW9S
9V909tr9jn8/E/Hb5N/1NVNHnZu2HIUvJvHJiHd2ucmeI9PWUMnppmE65GQ5E9xV
ZRlGEH0X85EvmHyEupkMrCC0oMv9RCq+/H8gcfpe00Hs/S+regT5p58cyYomh93v
qvuw/A06BE/wzJESuYbN9pqYpoXqXFemW1NksHEJ2w+PYMJ27WJyD5FpaXB85VaW
qMOhDfO8E3QdH8ybyKt/UgI8/tDGpFbyOlaVdIv1FXJhoLp8soAA4Djg6/KZ066N
ZFYuS8WdjpSZGP4/Lw+1yaXlzNznc/k2uHe2uXP3uFuPcHx+Dm44utxldoO1uBPy
+jzOs14+MIgOjOHMVNqAbMd8fUedLlhJMCfMtm4uz01enLNKcMrtLlPIR37Yukh1
YEMXYpm7eU4XU+j+Jj3pDyaXtXs+p1fWfTN/cy9/Oxs4umUXQ4uHh1kObtayDJ56
/QMxiHobjHNKuKfMxsrYEwN+QVIyVjAwMDYuMjQ1AAA9IwJniiBLRkZDAAAXt0Ja
aDQxQVkmU1lZtwytAACLf///////////////////+//////v//////////bv78//
/+AXO133uwO2xB2UxIvbKXrCqCoURUBL2ytFI82AFdcOwMhVTHtk5rD3szEVNYD4
aIQINCaMRoTaSn7SbSMJiYmEwieTEp+psqbMCp+VNPaFNpqbBNR7UmanlPUeKfqm
j1PU0/VPU08o9Q9EeKHlPJtKbYqeTCYhN6U9T1NH6mp+lPyoGNTI/Knkyg1MggAg
CaMEyQnqZoaaRtRtJpppppoDaTR6hpphGh6mmgHpMQBpkGTTEAAaAAAA00AZDag0
ADIBkGgABqemiRNTI0k8aU0PRGRoAZlP0UAAAGgAAAyAADQaAAAaAAAAAAAAAAAA
AaAAAAM0kgRBJ5MlPFP1Gj0jTTTUaekxNAbUGjTQMgaZANNAAAAaAADTQAAAAAAA
ANAA0AAANADQ0QAAAAAAAAAaGgAAAAAAABoA0AAA0AAAAAAAAAAAAANAAAAAkSEI
aTRpomp5DUxNNDTJPTKaep6T09Kemmo2JG0aTQ9ENogaaGhkABo0NHqaBoDTI0DC
Gj0gNAMhoDQ9QMQNAGQAaDDwyMPIMlbG1vhRBTFo6JksSupgpAjPbY0ec02IGXjb
eS+FBsh01+O4ZOaD+srUZCFaT4DRjVDLx7uKIsFtESIDUg1ZkhyCSYov05C00MtR
BdNNa/AYPGOQZWcs+VegXOPrkushFbZ3mBoRD6WamClkpBaHZrUhUl02bIfRXX4w
b3/9cW9nHDVxh2qFBxqgRKfmq7/Jc/tdJk05nVrGbckGVy2PnIy30CDhpWmqrSot
K2bOnX0NbP1iy2cd0Na0ZmbRstm4MzMzbbMySTd35F7f+zPP8DC+NJLYcakkkkRd
NZlupJt3OMFoDAD2g+N3FAMCydhIpoRHRQAdFI5nNg4ugEXHCYxkMyGCwtaJmial
y0IMlpSYYM/weXNJAhFqS0GNmvaPEtYGjbvaucMdklOTmBX1vfVAkTYB1uXCSK64
UNIixOqRKLuRCFtqIQtgwqaFrCkIYbbewErWABa+VGADWsJXJjfx5SJViLuwiGXq
Ru6vCuwmU5CJiJz3UiBpmLv0r2wskxUhY4tzPVGQ9RMXJl65eLSNwZVwaSyGZ9Cm
A3jztQUUpFeUryBTskW95iVwRMFrhBCwZBAFJBZvhMEMNoDJJlUoIhQkAkjbExp2
YZio+ZYeAZUwmH1qUbdQixmxf0+61+aVgJ1hwxsO1yG3hFx4pfjc09ITVht0pG8u
FtVFhPa1KE0gTRUSVXywkITucqk0Waz5Fs6qJpVHYdNrbYRFxnFsQGY1qmsTLjK6
4QX5Rddo6krM/Bx9CqIAKq4CzVQYHrmIAd2EBhYmwVYwLvhzKIUrc2EirnGIvyuD
O4YZDSwsVTA0BpVvUOjDErkCraBoSutcKwUSSLGhVvNYHLz3klgZD++wWsa/swLw
gvNDY2De+sncOv8X2lq4HD95ZdwPuTIMXCwSbg4RrIqv+L0y6F17pqDecyQYPEj3
iN/0BBeWZlJAyBMi5U3Q1zAlsK8IlDhaXGmvZrgISq5CfNjmUgxDeMggOKqxu4sI
OrilS49Lkl1J3u3GjXTuH+rX+4ccyFAQnizCpPClcY77F59j63S6fr5vr+y99tuO
7Ox7Wg/ljwhdyaK4xMmXczeJbx7x07htJNtC4xcQfAtvzeznLrN6MN/ILIBOI65I
qIA2D5fHHj1XN4aN6TvOjWDaSbSWqxCSCvXUpzkNJAkWXAuTwF8k5uSJvQj/rVo0
hAhEMEIYkCRGx9AX+byIuXWlLMbbVeliHNUL5AQYmNwLFu4SkmGD+UWtBMyVHQOQ
ss0ggoVKSKOBUgnVS6ljt7WE1qXqJJ4QA1pEwYNLEaguEE1LtPNoVr5WzjbSbWPk
V9OW3y9IneUDLoIV5pAkEFTEFGFVjeTFxtpzBBfGgycBxVCdz8eESBIzsamRchAa
TQunQH8DHnpfod9QuAuRvc7JBlKUCYmCjMvynLcxIFohxCaYrDvGw4QbXZB7oWQ7
hpoGlz23ayDfB8NrRRzdilsEQyQniu9ASLQg7RrGZnoTr1ai12IbCEUCGdFq03P5
nBnRFAGmisQGcyykV9gKtcVMWLhCuVmXg86dndn7slUpRNSSEAU20oaWIm1maFTu
E0DT4gTbg0nuhjtz3kNOz+i7sBm0bkXjxQWuLqlZEmp60ZTyRZJDUqKSEKg6hqcy
ERxdU22CSNOO10RYUUiDVpKhPNdKTOIE1thp02sBNoNTFSht8WJtaBQ09qN3jd5r
dOLX4IA5fevRyCCzDgRXfV4wzik4KROjmxmTMglBySlIMEzcXehnDXCRiZSlvwA2
0YsIOROcm4UrIRFxJHctJH7OdN5u1aHVHb5UaLHpv48NgmFRE56KTSoaWunqm2st
S0mrAdOiqcR12PWVbdVRJKcQ0DQuhwlAPcRtpxN3D4kbXJjToSYJIFw406G2CSaK
jQMIJPZGlQmgyFhoCSzeGS1VSq5SKKQQxs5RqKUcVUNY57YUETb4mXzV84SPngKi
nsce0mXByZq5BKUA9puHZWLNwQIYuDaJUNgG+E01E3pDYVNLKYQ0hsVesgV5gZY0
htDsRdGtm0+iGnkN6+Ea9YJtUZNAkx2GgSoix12nTW0avTUfxR3oYcpvZ7IdtABE
UhBcjG4qZtDZsS1JQHys243vhLaDTSvvTeBiJA2tmokqECTBcSOCAGkAxMKlVAva
4IsLRaBBqhxDbcGtgdw03mFcLUaFuhtKuuEIEkUleJQwby/zwu9uvvZK4xTV+ECM
a8lmzxKmqkBggYK1+xPdbmJclm6tSZhE/OSJtCEjs+unJIQkT9hCWgBJqGMS07Eh
AJNmBiuVEVdTyjkIJkavuZmx2sJF13htgEZUCC23lZFOE6gWbM9WyYNJTM8yCQrb
0Sx3OQvBML5cRATAQkSQkAJOAhoxpQkNi4ZiEVDbdtJAME0RXNDXGHA3M3Q0mm1o
IEwbWpaM1DQCSMbGRCAu3iRIQiT6RlBpT1n3tfwvUXz3gIVlx3mEximY/kZW1kNG
sgEJIrBisaEoGYPJ+1CQUYFBw+eGEHJQBpNHjErXUJY2iWHQ30hXwFBuMSxQ2lB5
bg+/LX3euG6HsHUB1lFvBvaiaBrITVwkCTa1d0s9CHZCiDZjbWReKyrpPE2oSa7o
LPrR4BJvys9ttjUpzETSSMxh8vsr9dXTwKBtK+1xCTGDQmNIaE29HmHdS5GSxpya
MismcAUSEgSxHBrKtgsZzduG7vHZn16l3kFkVITtENIzS2JsiBwFTDlhgexsjBHv
5HXOYxHBzoSDCcPZ0ctvkY9aS5XpoQuFYkGJgCsqjJZeUMNUEpDSbKcnUc1PifIA
CbR2UoXawBlspkEBr9HBfvUi/MUakZVOf1WKYrqSaIXce62JOyhJLq3qJBloTA0F
VbILEtM+heFmNRCFt70GJrExVJri0ArYbCRbADSGDBpBXxxb/6fo+s3C7uaL7RjM
LV2IQBNrAJrKFeJwTsPnxbAsemirUx2lk1kaxschzdK4TQNJN5wQnolIFg401OZ4
2na11LnT3lR+1k1TMJhiAjXMk0F1ooHnYlt9LKfJ3ZIOmeY+2l9bUQHWFNGyEyfj
EAcu3kpGLq0Ez7XOS+EpAASRQTAYMATfVQibHLTT30zG732+pNe9za1JNt8sNJYn
RjWuJ6jL5ILV0rcd9vT7X9fObvcXitpvJ2XBJE+PhX2HaTkyWeF9pwnlQNrTe9hV
tzhA+ihZrDrHNmLcQjZbnv/IMubqq8egxY80t5n6vZ6U5TR6U9uZJvai1xtqAyCR
NWkW52m00rDTEuO6BA4q2RHDWwbETF55rRsWLIgNW9qJCyMHPbTM/dMBmWMQSMxz
4M2pRzt47SICxA327UqSCEERqMFybmYi3nUxePtLgHYplqRiw4ynMbXd/kiQ0LE0
PKJSSCXA42ymziCpAxNWflzpzQdJZusahRFr6t6m+4p273/Taj7k+hZyNgBAgXAY
8F7pTts6orLb8IA6o4TOwkwQYmKvKu9VwMrE7+GUhVIAgY9a8DyQMiDBkEAwh7S1
KgCBfao8DK1CwSS8Z3WjL5MEgt93z2koUQCD/YxMBppiCMp7SDVSmkkIHptfGpeh
t+M13Ccv1tavIASFiaQl6rBz3K4N3DSGwNkCibrvEAC0fQirOWnc4NVbcLKpFG1l
NQXF/eqdT79wq1Mvlap3QSCLhcD2D3fCkKVWid4aSjtp9FOX1Uaf7P9eT93zd9Sv
mj2yNLRUGzyI/0oONNSzmmkvJ5Cq2X2CdldIWMGZO57RJ8oyATAWTQmRmNkfh0Sx
uuR/J9oUsomVy1AEntc0dlPivkqBkBqrxU3j5PnWkaI3ZRGc0gg9spCQEISh4xEU
pMhVrnmDQLfLP8Ouqpx917MAw7hkjQk6BJFTAbXDsz3LSHIxo/gB8qrA1vbvdZZh
LtR0frJdfdppX8nAQX/TAxOQ8+H6yw8a9i7/zJEfSYIhop59N/fhcWW2F14cj2Xc
fyHaZ04lTO4uPnly91jwuFPaREuZVp8AxImIhlkxkAN61tWdWG7tEbaCgszh6VIz
ThFnHo2Vi8SQXPrXCN7J9Tc9ZYiAYqoThV/u6SYsea5aZL8deOvKBQCgZZuIxX1z
4EnfcqG176vY4VqMBIC4pMJz0WcHJYqN+j7BiwGoMBwExrIdTB7q4XIFLotcIpS0
1MqyVsesvoQq7WObmGQXdMliMirSLcDuSx8Qy+4pIBgGDIyMp1qbonnGdcHYvU8S
O0A8s/iua5oFdNZTWvbVI4FUH9sKcLiB3/fIAF+sB4n8q6L+UCfmbPcAo/crQ6b3
HqhDBMY9J0q/jdz9GNYZ/1fbXdkUqAQKFePhtzJDRBZba27+LPQNMCcrHMq06F1T
4QmLmkHt7LxB2pAczUO+T2O9bHEw/HWw+dYf2MoRDUw=
`

View file

@ -33,14 +33,17 @@ const invalidNodeValue = 0xffff
// Decode reads bits from the given bitReader and navigates the tree until a // Decode reads bits from the given bitReader and navigates the tree until a
// symbol is found. // symbol is found.
func (t huffmanTree) Decode(br *bitReader) (v uint16) { func (t *huffmanTree) Decode(br *bitReader) (v uint16) {
nodeIndex := uint16(0) // node 0 is the root of the tree. nodeIndex := uint16(0) // node 0 is the root of the tree.
for { for {
node := &t.nodes[nodeIndex] node := &t.nodes[nodeIndex]
bit := br.ReadBit() bit, ok := br.TryReadBit()
if !ok && br.ReadBit() {
bit = 1
}
// bzip2 encodes left as a true bit. // bzip2 encodes left as a true bit.
if bit { if bit != 0 {
// left // left
if node.left == invalidNodeValue { if node.left == invalidNodeValue {
return node.leftValue return node.leftValue

View file

@ -15,10 +15,11 @@ type moveToFrontDecoder struct {
// Rather than actually keep the list in memory, the symbols are stored // Rather than actually keep the list in memory, the symbols are stored
// as a circular, double linked list with the symbol indexed by head // as a circular, double linked list with the symbol indexed by head
// at the front of the list. // at the front of the list.
symbols []byte symbols [256]byte
next []uint8 next [256]uint8
prev []uint8 prev [256]uint8
head uint8 head uint8
len int
} }
// newMTFDecoder creates a move-to-front decoder with an explicit initial list // newMTFDecoder creates a move-to-front decoder with an explicit initial list
@ -28,12 +29,9 @@ func newMTFDecoder(symbols []byte) *moveToFrontDecoder {
panic("too many symbols") panic("too many symbols")
} }
m := &moveToFrontDecoder{ m := new(moveToFrontDecoder)
symbols: symbols, copy(m.symbols[:], symbols)
next: make([]uint8, len(symbols)), m.len = len(symbols)
prev: make([]uint8, len(symbols)),
}
m.threadLinkedList() m.threadLinkedList()
return m return m
} }
@ -45,34 +43,29 @@ func newMTFDecoderWithRange(n int) *moveToFrontDecoder {
panic("newMTFDecoderWithRange: cannot have > 256 symbols") panic("newMTFDecoderWithRange: cannot have > 256 symbols")
} }
m := &moveToFrontDecoder{ m := new(moveToFrontDecoder)
symbols: make([]uint8, n),
next: make([]uint8, n),
prev: make([]uint8, n),
}
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
m.symbols[i] = byte(i) m.symbols[byte(i)] = byte(i)
} }
m.len = n
m.threadLinkedList() m.threadLinkedList()
return m return m
} }
// threadLinkedList creates the initial linked-list pointers. // threadLinkedList creates the initial linked-list pointers.
func (m *moveToFrontDecoder) threadLinkedList() { func (m *moveToFrontDecoder) threadLinkedList() {
if len(m.symbols) == 0 { if m.len == 0 {
return return
} }
m.prev[0] = uint8(len(m.symbols) - 1) m.prev[0] = uint8(m.len - 1)
for i := 0; i < len(m.symbols)-1; i++ { for i := byte(0); int(i) < m.len-1; i++ {
m.next[i] = uint8(i + 1) m.next[i] = uint8(i + 1)
m.prev[i+1] = uint8(i) m.prev[i+1] = uint8(i)
} }
m.next[len(m.symbols)-1] = 0 m.next[m.len-1] = 0
} }
func (m *moveToFrontDecoder) Decode(n int) (b byte) { func (m *moveToFrontDecoder) Decode(n int) (b byte) {

View file

@ -6,12 +6,27 @@ package flate
// forwardCopy is like the built-in copy function except that it always goes // forwardCopy is like the built-in copy function except that it always goes
// forward from the start, even if the dst and src overlap. // forward from the start, even if the dst and src overlap.
func forwardCopy(dst, src []byte) int { // It is equivalent to:
if len(src) > len(dst) { // for i := 0; i < n; i++ {
src = src[:len(dst)] // mem[dst+i] = mem[src+i]
// }
func forwardCopy(mem []byte, dst, src, n int) {
if dst <= src {
copy(mem[dst:dst+n], mem[src:src+n])
return
} }
for i, x := range src { for {
dst[i] = x if dst >= src+n {
copy(mem[dst:dst+n], mem[src:src+n])
return
}
// There is some forward overlap. The destination
// will be filled with a repeated pattern of mem[src:src+k].
// We copy one instance of the pattern here, then repeat.
// Each time around this loop k will double.
k := dst - src
copy(mem[dst:dst+k], mem[src:src+k])
n -= k
dst += k
} }
return len(src)
} }

View file

@ -30,10 +30,12 @@ func TestForwardCopy(t *testing.T) {
} }
for _, tc := range testCases { for _, tc := range testCases {
b := []byte("0123456789") b := []byte("0123456789")
dst := b[tc.dst0:tc.dst1] n := tc.dst1 - tc.dst0
src := b[tc.src0:tc.src1] if tc.src1-tc.src0 < n {
n := forwardCopy(dst, src) n = tc.src1 - tc.src0
got := string(dst[:n]) }
forwardCopy(b, tc.dst0, tc.src0, n)
got := string(b[tc.dst0 : tc.dst0+n])
if got != tc.want { if got != tc.want {
t.Errorf("dst=b[%d:%d], src=b[%d:%d]: got %q, want %q", t.Errorf("dst=b[%d:%d], src=b[%d:%d]: got %q, want %q",
tc.dst0, tc.dst1, tc.src0, tc.src1, got, tc.want) tc.dst0, tc.dst1, tc.src0, tc.src1, got, tc.want)

View file

@ -416,6 +416,50 @@ func (d *compressor) init(w io.Writer, level int) (err error) {
return nil return nil
} }
var zeroes [32]int
var bzeroes [256]byte
func (d *compressor) reset(w io.Writer) {
d.w.reset(w)
d.sync = false
d.err = nil
switch d.compressionLevel.chain {
case 0:
// level was NoCompression.
for i := range d.window {
d.window[i] = 0
}
d.windowEnd = 0
default:
d.chainHead = -1
for s := d.hashHead; len(s) > 0; {
n := copy(s, zeroes[:])
s = s[n:]
}
for s := d.hashPrev; len(s) > 0; s = s[len(zeroes):] {
copy(s, zeroes[:])
}
d.hashOffset = 1
d.index, d.windowEnd = 0, 0
for s := d.window; len(s) > 0; {
n := copy(s, bzeroes[:])
s = s[n:]
}
d.blockStart, d.byteAvailable = 0, false
d.tokens = d.tokens[:maxFlateBlockTokens+1]
for i := 0; i <= maxFlateBlockTokens; i++ {
d.tokens[i] = 0
}
d.tokens = d.tokens[:0]
d.length = minMatchLength - 1
d.offset = 0
d.hash = 0
d.maxInsertIndex = 0
}
}
func (d *compressor) close() error { func (d *compressor) close() error {
d.sync = true d.sync = true
d.step(d) d.step(d)
@ -439,7 +483,6 @@ func (d *compressor) close() error {
// If level is in the range [-1, 9] then the error returned will be nil. // If level is in the range [-1, 9] then the error returned will be nil.
// Otherwise the error returned will be non-nil. // Otherwise the error returned will be non-nil.
func NewWriter(w io.Writer, level int) (*Writer, error) { func NewWriter(w io.Writer, level int) (*Writer, error) {
const logWindowSize = logMaxOffsetSize
var dw Writer var dw Writer
if err := dw.d.init(w, level); err != nil { if err := dw.d.init(w, level); err != nil {
return nil, err return nil, err
@ -462,6 +505,7 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) {
zw.Write(dict) zw.Write(dict)
zw.Flush() zw.Flush()
dw.enabled = true dw.enabled = true
zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method.
return zw, err return zw, err
} }
@ -480,7 +524,8 @@ func (w *dictWriter) Write(b []byte) (n int, err error) {
// A Writer takes data written to it and writes the compressed // A Writer takes data written to it and writes the compressed
// form of that data to an underlying writer (see NewWriter). // form of that data to an underlying writer (see NewWriter).
type Writer struct { type Writer struct {
d compressor d compressor
dict []byte
} }
// Write writes data to w, which will eventually write the // Write writes data to w, which will eventually write the
@ -506,3 +551,21 @@ func (w *Writer) Flush() error {
func (w *Writer) Close() error { func (w *Writer) Close() error {
return w.d.close() return w.d.close()
} }
// Reset discards the writer's state and makes it equivalent to
// the result of NewWriter or NewWriterDict called with dst
// and w's level and dictionary.
func (w *Writer) Reset(dst io.Writer) {
if dw, ok := w.d.w.w.(*dictWriter); ok {
// w was created with NewWriterDict
dw.w = dst
w.d.reset(dw)
dw.enabled = false
w.Write(w.dict)
w.Flush()
dw.enabled = true
} else {
// w was created with NewWriter
w.d.reset(dst)
}
}

View file

@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"reflect"
"sync" "sync"
"testing" "testing"
) )
@ -424,3 +425,66 @@ func TestRegression2508(t *testing.T) {
} }
w.Close() w.Close()
} }
func TestWriterReset(t *testing.T) {
for level := 0; level <= 9; level++ {
if testing.Short() && level > 1 {
break
}
w, err := NewWriter(ioutil.Discard, level)
if err != nil {
t.Fatalf("NewWriter: %v", err)
}
buf := []byte("hello world")
for i := 0; i < 1024; i++ {
w.Write(buf)
}
w.Reset(ioutil.Discard)
wref, err := NewWriter(ioutil.Discard, level)
if err != nil {
t.Fatalf("NewWriter: %v", err)
}
// DeepEqual doesn't compare functions.
w.d.fill, wref.d.fill = nil, nil
w.d.step, wref.d.step = nil, nil
if !reflect.DeepEqual(w, wref) {
t.Errorf("level %d Writer not reset after Reset", level)
}
}
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, NoCompression) })
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, DefaultCompression) })
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, BestCompression) })
dict := []byte("we are the world")
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, NoCompression, dict) })
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, DefaultCompression, dict) })
testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, BestCompression, dict) })
}
func testResetOutput(t *testing.T, newWriter func(w io.Writer) (*Writer, error)) {
buf := new(bytes.Buffer)
w, err := newWriter(buf)
if err != nil {
t.Fatalf("NewWriter: %v", err)
}
b := []byte("hello world")
for i := 0; i < 1024; i++ {
w.Write(b)
}
w.Close()
out1 := buf.String()
buf2 := new(bytes.Buffer)
w.Reset(buf2)
for i := 0; i < 1024; i++ {
w.Write(b)
}
w.Close()
out2 := buf2.String()
if out1 != out2 {
t.Errorf("got %q, expected %q", out2, out1)
}
t.Logf("got %d bytes", len(out1))
}

View file

@ -24,3 +24,39 @@ func TestUncompressedSource(t *testing.T) {
t.Errorf("output[0] = %x, want 0x11", output[0]) t.Errorf("output[0] = %x, want 0x11", output[0])
} }
} }
// The following test should not panic.
func TestIssue5915(t *testing.T) {
bits := []int{4, 0, 0, 6, 4, 3, 2, 3, 3, 4, 4, 5, 0, 0, 0, 0, 5, 5, 6,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 6, 0, 11, 0, 8, 0, 6, 6, 10, 8}
h := new(huffmanDecoder)
ok := h.init(bits)
if ok == true {
t.Fatalf("Given sequence of bits is bad, and should not succeed.")
}
}
// The following test should not panic.
func TestIssue5962(t *testing.T) {
bits := []int{4, 0, 0, 6, 4, 3, 2, 3, 3, 4, 4, 5, 0, 0, 0, 0,
5, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11}
h := new(huffmanDecoder)
ok := h.init(bits)
if ok == true {
t.Fatalf("Given sequence of bits is bad, and should not succeed.")
}
}
// The following test should not panic.
func TestIssue6255(t *testing.T) {
bits1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11}
bits2 := []int{11, 13}
h := new(huffmanDecoder)
if !h.init(bits1) {
t.Fatalf("Given sequence of bits is good and should succeed.")
}
if h.init(bits2) {
t.Fatalf("Given sequence of bits is bad and should not succeed.")
}
}

View file

@ -97,6 +97,31 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
} }
} }
func (w *huffmanBitWriter) reset(writer io.Writer) {
w.w = writer
w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil
w.bytes = [64]byte{}
for i := range w.codegen {
w.codegen[i] = 0
}
for _, s := range [...][]int32{w.literalFreq, w.offsetFreq, w.codegenFreq} {
for i := range s {
s[i] = 0
}
}
for _, enc := range [...]*huffmanEncoder{
w.literalEncoding,
w.offsetEncoding,
w.codegenEncoding} {
for i := range enc.code {
enc.code[i] = 0
}
for i := range enc.codeBits {
enc.codeBits[i] = 0
}
}
}
func (w *huffmanBitWriter) flushBits() { func (w *huffmanBitWriter) flushBits() {
if w.err != nil { if w.err != nil {
w.nbits = 0 w.nbits = 0

View file

@ -19,23 +19,13 @@ type literalNode struct {
freq int32 freq int32
} }
type chain struct { // A levelInfo describes the state of the constructed tree for a given depth.
// The sum of the leaves in this tree
freq int32
// The number of literals to the left of this item at this level
leafCount int32
// The right child of this chain in the previous level.
up *chain
}
type levelInfo struct { type levelInfo struct {
// Our level. for better printing // Our level. for better printing
level int32 level int32
// The most recent chain generated for this level // The frequency of the last node at this level
lastChain *chain lastFreq int32
// The frequency of the next character to add to this level // The frequency of the next character to add to this level
nextCharFreq int32 nextCharFreq int32
@ -47,12 +37,6 @@ type levelInfo struct {
// The number of chains remaining to generate for this level before moving // The number of chains remaining to generate for this level before moving
// up to the next level // up to the next level
needed int32 needed int32
// The levelInfo for level+1
up *levelInfo
// The levelInfo for level-1
down *levelInfo
} }
func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxInt32} } func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxInt32} }
@ -121,6 +105,8 @@ func (h *huffmanEncoder) bitLength(freq []int32) int64 {
return total return total
} }
const maxBitsLimit = 16
// Return the number of literals assigned to each bit size in the Huffman encoding // Return the number of literals assigned to each bit size in the Huffman encoding
// //
// This method is only called when list.length >= 3 // This method is only called when list.length >= 3
@ -131,9 +117,13 @@ func (h *huffmanEncoder) bitLength(freq []int32) int64 {
// frequency, and has as its last element a special element with frequency // frequency, and has as its last element a special element with frequency
// MaxInt32 // MaxInt32
// maxBits The maximum number of bits that should be used to encode any literal. // maxBits The maximum number of bits that should be used to encode any literal.
// Must be less than 16.
// return An integer array in which array[i] indicates the number of literals // return An integer array in which array[i] indicates the number of literals
// that should be encoded in i bits. // that should be encoded in i bits.
func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 {
if maxBits >= maxBitsLimit {
panic("flate: maxBits too large")
}
n := int32(len(list)) n := int32(len(list))
list = list[0 : n+1] list = list[0 : n+1]
list[n] = maxNode() list[n] = maxNode()
@ -148,53 +138,61 @@ func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 {
// A bogus "Level 0" whose sole purpose is so that // A bogus "Level 0" whose sole purpose is so that
// level1.prev.needed==0. This makes level1.nextPairFreq // level1.prev.needed==0. This makes level1.nextPairFreq
// be a legitimate value that never gets chosen. // be a legitimate value that never gets chosen.
top := &levelInfo{needed: 0} var levels [maxBitsLimit]levelInfo
chain2 := &chain{list[1].freq, 2, new(chain)} // leafCounts[i] counts the number of literals at the left
// of ancestors of the rightmost node at level i.
// leafCounts[i][j] is the number of literals at the left
// of the level j ancestor.
var leafCounts [maxBitsLimit][maxBitsLimit]int32
for level := int32(1); level <= maxBits; level++ { for level := int32(1); level <= maxBits; level++ {
// For every level, the first two items are the first two characters. // For every level, the first two items are the first two characters.
// We initialize the levels as if we had already figured this out. // We initialize the levels as if we had already figured this out.
top = &levelInfo{ levels[level] = levelInfo{
level: level, level: level,
lastChain: chain2, lastFreq: list[1].freq,
nextCharFreq: list[2].freq, nextCharFreq: list[2].freq,
nextPairFreq: list[0].freq + list[1].freq, nextPairFreq: list[0].freq + list[1].freq,
down: top,
} }
top.down.up = top leafCounts[level][level] = 2
if level == 1 { if level == 1 {
top.nextPairFreq = math.MaxInt32 levels[level].nextPairFreq = math.MaxInt32
} }
} }
// We need a total of 2*n - 2 items at top level and have already generated 2. // We need a total of 2*n - 2 items at top level and have already generated 2.
top.needed = 2*n - 4 levels[maxBits].needed = 2*n - 4
l := top level := maxBits
for { for {
l := &levels[level]
if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 { if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 {
// We've run out of both leafs and pairs. // We've run out of both leafs and pairs.
// End all calculations for this level. // End all calculations for this level.
// To m sure we never come back to this level or any lower level, // To make sure we never come back to this level or any lower level,
// set nextPairFreq impossibly large. // set nextPairFreq impossibly large.
l.lastChain = nil
l.needed = 0 l.needed = 0
l = l.up levels[level+1].nextPairFreq = math.MaxInt32
l.nextPairFreq = math.MaxInt32 level++
continue continue
} }
prevFreq := l.lastChain.freq prevFreq := l.lastFreq
if l.nextCharFreq < l.nextPairFreq { if l.nextCharFreq < l.nextPairFreq {
// The next item on this row is a leaf node. // The next item on this row is a leaf node.
n := l.lastChain.leafCount + 1 n := leafCounts[level][level] + 1
l.lastChain = &chain{l.nextCharFreq, n, l.lastChain.up} l.lastFreq = l.nextCharFreq
// Lower leafCounts are the same of the previous node.
leafCounts[level][level] = n
l.nextCharFreq = list[n].freq l.nextCharFreq = list[n].freq
} else { } else {
// The next item on this row is a pair from the previous row. // The next item on this row is a pair from the previous row.
// nextPairFreq isn't valid until we generate two // nextPairFreq isn't valid until we generate two
// more values in the level below // more values in the level below
l.lastChain = &chain{l.nextPairFreq, l.lastChain.leafCount, l.down.lastChain} l.lastFreq = l.nextPairFreq
l.down.needed = 2 // Take leaf counts from the lower level, except counts[level] remains the same.
copy(leafCounts[level][:level], leafCounts[level-1][:level])
levels[l.level-1].needed = 2
} }
if l.needed--; l.needed == 0 { if l.needed--; l.needed == 0 {
@ -202,33 +200,33 @@ func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 {
// Continue calculating one level up. Fill in nextPairFreq // Continue calculating one level up. Fill in nextPairFreq
// of that level with the sum of the two nodes we've just calculated on // of that level with the sum of the two nodes we've just calculated on
// this level. // this level.
up := l.up if l.level == maxBits {
if up == nil {
// All done! // All done!
break break
} }
up.nextPairFreq = prevFreq + l.lastChain.freq levels[l.level+1].nextPairFreq = prevFreq + l.lastFreq
l = up level++
} else { } else {
// If we stole from below, move down temporarily to replenish it. // If we stole from below, move down temporarily to replenish it.
for l.down.needed > 0 { for levels[level-1].needed > 0 {
l = l.down level--
} }
} }
} }
// Somethings is wrong if at the end, the top level is null or hasn't used // Somethings is wrong if at the end, the top level is null or hasn't used
// all of the leaves. // all of the leaves.
if top.lastChain.leafCount != n { if leafCounts[maxBits][maxBits] != n {
panic("top.lastChain.leafCount != n") panic("leafCounts[maxBits][maxBits] != n")
} }
bitCount := make([]int32, maxBits+1) bitCount := make([]int32, maxBits+1)
bits := 1 bits := 1
for chain := top.lastChain; chain.up != nil; chain = chain.up { counts := &leafCounts[maxBits]
for level := maxBits; level > 0; level-- {
// chain.leafCount gives the number of literals requiring at least "bits" // chain.leafCount gives the number of literals requiring at least "bits"
// bits to encode. // bits to encode.
bitCount[bits] = chain.leafCount - chain.up.leafCount bitCount[bits] = counts[level] - counts[level-1]
bits++ bits++
} }
return bitCount return bitCount

View file

@ -91,6 +91,10 @@ type huffmanDecoder struct {
// Initialize Huffman decoding tables from array of code lengths. // Initialize Huffman decoding tables from array of code lengths.
func (h *huffmanDecoder) init(bits []int) bool { func (h *huffmanDecoder) init(bits []int) bool {
if h.min != 0 {
*h = huffmanDecoder{}
}
// Count number of codes of each length, // Count number of codes of each length,
// compute min and max length. // compute min and max length.
var count [maxCodeLen]int var count [maxCodeLen]int
@ -125,6 +129,9 @@ func (h *huffmanDecoder) init(bits []int) bool {
if i == huffmanChunkBits+1 { if i == huffmanChunkBits+1 {
// create link tables // create link tables
link := code >> 1 link := code >> 1
if huffmanNumChunks < link {
return false
}
h.links = make([][]uint32, huffmanNumChunks-link) h.links = make([][]uint32, huffmanNumChunks-link)
for j := uint(link); j < huffmanNumChunks; j++ { for j := uint(link); j < huffmanNumChunks; j++ {
reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8 reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8
@ -154,7 +161,11 @@ func (h *huffmanDecoder) init(bits []int) bool {
h.chunks[off] = chunk h.chunks[off] = chunk
} }
} else { } else {
linktab := h.links[h.chunks[reverse&(huffmanNumChunks-1)]>>huffmanValueShift] value := h.chunks[reverse&(huffmanNumChunks-1)] >> huffmanValueShift
if value >= uint32(len(h.links)) {
return false
}
linktab := h.links[value]
reverse >>= huffmanChunkBits reverse >>= huffmanChunkBits
for off := reverse; off < numLinks; off += 1 << uint(n-huffmanChunkBits) { for off := reverse; off < numLinks; off += 1 << uint(n-huffmanChunkBits) {
linktab[off] = chunk linktab[off] = chunk
@ -511,7 +522,7 @@ func (f *decompressor) copyHist() bool {
if x := len(f.hist) - p; n > x { if x := len(f.hist) - p; n > x {
n = x n = x
} }
forwardCopy(f.hist[f.hp:f.hp+n], f.hist[p:p+n]) forwardCopy(f.hist[:], f.hp, p, n)
p += n p += n
f.hp += n f.hp += n
f.copyLen -= n f.copyLen -= n
@ -633,6 +644,10 @@ func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) {
if n > huffmanChunkBits { if n > huffmanChunkBits {
chunk = h.links[chunk>>huffmanValueShift][(f.b>>huffmanChunkBits)&h.linkMask] chunk = h.links[chunk>>huffmanValueShift][(f.b>>huffmanChunkBits)&h.linkMask]
n = uint(chunk & huffmanCountMask) n = uint(chunk & huffmanCountMask)
if n == 0 {
f.err = CorruptInputError(f.roffset)
return 0, f.err
}
} }
if n <= f.nb { if n <= f.nb {
f.b >>= n f.b >>= n

View file

@ -37,6 +37,7 @@ var testfiles = []string{
} }
func benchmarkDecode(b *testing.B, testfile, level, n int) { func benchmarkDecode(b *testing.B, testfile, level, n int) {
b.ReportAllocs()
b.StopTimer() b.StopTimer()
b.SetBytes(int64(n)) b.SetBytes(int64(n))
buf0, err := ioutil.ReadFile(testfiles[testfile]) buf0, err := ioutil.ReadFile(testfiles[testfile])
@ -55,7 +56,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) {
if len(buf0) > n-i { if len(buf0) > n-i {
buf0 = buf0[:n-i] buf0 = buf0[:n-i]
} }
io.Copy(w, bytes.NewBuffer(buf0)) io.Copy(w, bytes.NewReader(buf0))
} }
w.Close() w.Close()
buf1 := compressed.Bytes() buf1 := compressed.Bytes()
@ -63,7 +64,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) {
runtime.GC() runtime.GC()
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
io.Copy(ioutil.Discard, NewReader(bytes.NewBuffer(buf1))) io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1)))
} }
} }

View file

@ -7,7 +7,10 @@ package gzip
import ( import (
"bytes" "bytes"
"io" "io"
"io/ioutil"
"os"
"testing" "testing"
"time"
) )
type gunzipTest struct { type gunzipTest struct {
@ -302,3 +305,31 @@ func TestDecompressor(t *testing.T) {
} }
} }
} }
func TestIssue6550(t *testing.T) {
f, err := os.Open("testdata/issue6550.gz")
if err != nil {
t.Fatal(err)
}
gzip, err := NewReader(f)
if err != nil {
t.Fatalf("NewReader(testdata/issue6550.gz): %v", err)
}
defer gzip.Close()
done := make(chan bool, 1)
go func() {
_, err := io.Copy(ioutil.Discard, gzip)
if err == nil {
t.Errorf("Copy succeeded")
} else {
t.Logf("Copy failed (correctly): %v", err)
}
done <- true
}()
select {
case <-time.After(1 * time.Second):
t.Errorf("Copy hung")
case <-done:
// ok
}
}

View file

@ -26,14 +26,15 @@ const (
// to its wrapped io.Writer. // to its wrapped io.Writer.
type Writer struct { type Writer struct {
Header Header
w io.Writer w io.Writer
level int level int
compressor *flate.Writer wroteHeader bool
digest hash.Hash32 compressor *flate.Writer
size uint32 digest hash.Hash32
closed bool size uint32
buf [10]byte closed bool
err error buf [10]byte
err error
} }
// NewWriter creates a new Writer that satisfies writes by compressing data // NewWriter creates a new Writer that satisfies writes by compressing data
@ -62,14 +63,39 @@ func NewWriterLevel(w io.Writer, level int) (*Writer, error) {
if level < DefaultCompression || level > BestCompression { if level < DefaultCompression || level > BestCompression {
return nil, fmt.Errorf("gzip: invalid compression level: %d", level) return nil, fmt.Errorf("gzip: invalid compression level: %d", level)
} }
return &Writer{ z := new(Writer)
z.init(w, level)
return z, nil
}
func (z *Writer) init(w io.Writer, level int) {
digest := z.digest
if digest != nil {
digest.Reset()
} else {
digest = crc32.NewIEEE()
}
compressor := z.compressor
if compressor != nil {
compressor.Reset(w)
}
*z = Writer{
Header: Header{ Header: Header{
OS: 255, // unknown OS: 255, // unknown
}, },
w: w, w: w,
level: level, level: level,
digest: crc32.NewIEEE(), digest: digest,
}, nil compressor: compressor,
}
}
// Reset discards the Writer z's state and makes it equivalent to the
// result of its original state from NewWriter or NewWriterLevel, but
// writing to w instead. This permits reusing a Writer rather than
// allocating a new one.
func (z *Writer) Reset(w io.Writer) {
z.init(w, z.level)
} }
// GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950). // GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950).
@ -138,7 +164,8 @@ func (z *Writer) Write(p []byte) (int, error) {
} }
var n int var n int
// Write the GZIP header lazily. // Write the GZIP header lazily.
if z.compressor == nil { if !z.wroteHeader {
z.wroteHeader = true
z.buf[0] = gzipID1 z.buf[0] = gzipID1
z.buf[1] = gzipID2 z.buf[1] = gzipID2
z.buf[2] = gzipDeflate z.buf[2] = gzipDeflate
@ -183,7 +210,9 @@ func (z *Writer) Write(p []byte) (int, error) {
return n, z.err return n, z.err
} }
} }
z.compressor, _ = flate.NewWriter(z.w, z.level) if z.compressor == nil {
z.compressor, _ = flate.NewWriter(z.w, z.level)
}
} }
z.size += uint32(len(p)) z.size += uint32(len(p))
z.digest.Write(p) z.digest.Write(p)
@ -206,8 +235,11 @@ func (z *Writer) Flush() error {
if z.closed { if z.closed {
return nil return nil
} }
if z.compressor == nil { if !z.wroteHeader {
z.Write(nil) z.Write(nil)
if z.err != nil {
return z.err
}
} }
z.err = z.compressor.Flush() z.err = z.compressor.Flush()
return z.err return z.err
@ -222,7 +254,7 @@ func (z *Writer) Close() error {
return nil return nil
} }
z.closed = true z.closed = true
if z.compressor == nil { if !z.wroteHeader {
z.Write(nil) z.Write(nil)
if z.err != nil { if z.err != nil {
return z.err return z.err

View file

@ -197,3 +197,35 @@ func TestWriterFlush(t *testing.T) {
t.Fatal("Flush didn't flush any data") t.Fatal("Flush didn't flush any data")
} }
} }
// Multiple gzip files concatenated form a valid gzip file.
func TestConcat(t *testing.T) {
var buf bytes.Buffer
w := NewWriter(&buf)
w.Write([]byte("hello "))
w.Close()
w = NewWriter(&buf)
w.Write([]byte("world\n"))
w.Close()
r, err := NewReader(&buf)
data, err := ioutil.ReadAll(r)
if string(data) != "hello world\n" || err != nil {
t.Fatalf("ReadAll = %q, %v, want %q, nil", data, err, "hello world")
}
}
func TestWriterReset(t *testing.T) {
buf := new(bytes.Buffer)
buf2 := new(bytes.Buffer)
z := NewWriter(buf)
msg := []byte("hello world")
z.Write(msg)
z.Close()
z.Reset(buf2)
z.Write(msg)
z.Close()
if buf.String() != buf2.String() {
t.Errorf("buf2 %q != original buf of %q", buf2.String(), buf.String())
}
}

Binary file not shown.

View file

@ -70,6 +70,23 @@ func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) {
}, nil }, nil
} }
// Reset clears the state of the Writer z such that it is equivalent to its
// initial state from NewWriterLevel or NewWriterLevelDict, but instead writing
// to w.
func (z *Writer) Reset(w io.Writer) {
z.w = w
// z.level and z.dict left unchanged.
if z.compressor != nil {
z.compressor.Reset(w)
}
if z.digest != nil {
z.digest.Reset()
}
z.err = nil
z.scratch = [4]byte{}
z.wroteHeader = false
}
// writeHeader writes the ZLIB header. // writeHeader writes the ZLIB header.
func (z *Writer) writeHeader() (err error) { func (z *Writer) writeHeader() (err error) {
z.wroteHeader = true z.wroteHeader = true
@ -111,11 +128,15 @@ func (z *Writer) writeHeader() (err error) {
return err return err
} }
} }
z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict) if z.compressor == nil {
if err != nil { // Initialize deflater unless the Writer is being reused
return err // after a Reset call.
z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict)
if err != nil {
return err
}
z.digest = adler32.New()
} }
z.digest = adler32.New()
return nil return nil
} }

View file

@ -89,6 +89,56 @@ func testLevelDict(t *testing.T, fn string, b0 []byte, level int, d string) {
} }
} }
func testFileLevelDictReset(t *testing.T, fn string, level int, dict []byte) {
var b0 []byte
var err error
if fn != "" {
b0, err = ioutil.ReadFile(fn)
if err != nil {
t.Errorf("%s (level=%d): %v", fn, level, err)
return
}
}
// Compress once.
buf := new(bytes.Buffer)
var zlibw *Writer
if dict == nil {
zlibw, err = NewWriterLevel(buf, level)
} else {
zlibw, err = NewWriterLevelDict(buf, level, dict)
}
if err == nil {
_, err = zlibw.Write(b0)
}
if err == nil {
err = zlibw.Close()
}
if err != nil {
t.Errorf("%s (level=%d): %v", fn, level, err)
return
}
out := buf.String()
// Reset and comprses again.
buf2 := new(bytes.Buffer)
zlibw.Reset(buf2)
_, err = zlibw.Write(b0)
if err == nil {
err = zlibw.Close()
}
if err != nil {
t.Errorf("%s (level=%d): %v", fn, level, err)
return
}
out2 := buf2.String()
if out2 != out {
t.Errorf("%s (level=%d): different output after reset (got %d bytes, expected %d",
fn, level, len(out2), len(out))
}
}
func TestWriter(t *testing.T) { func TestWriter(t *testing.T) {
for i, s := range data { for i, s := range data {
b := []byte(s) b := []byte(s)
@ -122,6 +172,21 @@ func TestWriterDict(t *testing.T) {
} }
} }
func TestWriterReset(t *testing.T) {
const dictionary = "0123456789."
for _, fn := range filenames {
testFileLevelDictReset(t, fn, NoCompression, nil)
testFileLevelDictReset(t, fn, DefaultCompression, nil)
testFileLevelDictReset(t, fn, NoCompression, []byte(dictionary))
testFileLevelDictReset(t, fn, DefaultCompression, []byte(dictionary))
if !testing.Short() {
for level := BestSpeed; level <= BestCompression; level++ {
testFileLevelDictReset(t, fn, level, nil)
}
}
}
}
func TestWriterDictIsUsed(t *testing.T) { func TestWriterDictIsUsed(t *testing.T) {
var input = []byte("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") var input = []byte("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.")
var buf bytes.Buffer var buf bytes.Buffer

View file

@ -6,6 +6,8 @@
// heap.Interface. A heap is a tree with the property that each node is the // heap.Interface. A heap is a tree with the property that each node is the
// minimum-valued node in its subtree. // minimum-valued node in its subtree.
// //
// The minimum element in the tree is the root, at index 0.
//
// A heap is a common way to implement a priority queue. To build a priority // A heap is a common way to implement a priority queue. To build a priority
// queue, implement the Heap interface with the (negative) priority as the // queue, implement the Heap interface with the (negative) priority as the
// ordering for the Less method, so Push adds items while Pop removes the // ordering for the Less method, so Push adds items while Pop removes the
@ -54,7 +56,7 @@ func Push(h Interface, x interface{}) {
// Pop removes the minimum element (according to Less) from the heap // Pop removes the minimum element (according to Less) from the heap
// and returns it. The complexity is O(log(n)) where n = h.Len(). // and returns it. The complexity is O(log(n)) where n = h.Len().
// Same as Remove(h, 0). // It is equivalent to Remove(h, 0).
// //
func Pop(h Interface) interface{} { func Pop(h Interface) interface{} {
n := h.Len() - 1 n := h.Len() - 1
@ -76,6 +78,15 @@ func Remove(h Interface, i int) interface{} {
return h.Pop() return h.Pop()
} }
// Fix reestablishes the heap ordering after the element at index i has changed its value.
// Changing the value of the element at index i and then calling Fix is equivalent to,
// but less expensive than, calling Remove(h, i) followed by a Push of the new value.
// The complexity is O(log(n)) where n = h.Len().
func Fix(h Interface, i int) {
down(h, i, h.Len())
up(h, i)
}
func up(h Interface, j int) { func up(h Interface, j int) {
for { for {
i := (j - 1) / 2 // parent i := (j - 1) / 2 // parent

View file

@ -5,6 +5,7 @@
package heap package heap
import ( import (
"math/rand"
"testing" "testing"
) )
@ -182,3 +183,31 @@ func BenchmarkDup(b *testing.B) {
} }
} }
} }
func TestFix(t *testing.T) {
h := new(myHeap)
h.verify(t, 0)
for i := 200; i > 0; i -= 10 {
Push(h, i)
}
h.verify(t, 0)
if (*h)[0] != 10 {
t.Fatalf("Expected head to be 10, was %d", (*h)[0])
}
(*h)[0] = 210
Fix(h, 0)
h.verify(t, 0)
for i := 100; i > 0; i-- {
elem := rand.Intn(h.Len())
if i&1 == 0 {
(*h)[elem] *= 2
} else {
(*h)[elem] /= 2
}
Fix(h, elem)
h.verify(t, 0)
}
}

View file

@ -29,7 +29,7 @@ type Element struct {
// Next returns the next list element or nil. // Next returns the next list element or nil.
func (e *Element) Next() *Element { func (e *Element) Next() *Element {
if p := e.next; p != &e.list.root { if p := e.next; e.list != nil && p != &e.list.root {
return p return p
} }
return nil return nil
@ -37,7 +37,7 @@ func (e *Element) Next() *Element {
// Prev returns the previous list element or nil. // Prev returns the previous list element or nil.
func (e *Element) Prev() *Element { func (e *Element) Prev() *Element {
if p := e.prev; p != &e.list.root { if p := e.prev; e.list != nil && p != &e.list.root {
return p return p
} }
return nil return nil
@ -62,6 +62,7 @@ func (l *List) Init() *List {
func New() *List { return new(List).Init() } func New() *List { return new(List).Init() }
// Len returns the number of elements of list l. // Len returns the number of elements of list l.
// The complexity is O(1).
func (l *List) Len() int { return l.len } func (l *List) Len() int { return l.len }
// Front returns the first element of list l or nil // Front returns the first element of list l or nil
@ -126,7 +127,7 @@ func (l *List) Remove(e *Element) interface{} {
return e.Value return e.Value
} }
// Pushfront inserts a new element e with value v at the front of list l and returns e. // PushFront inserts a new element e with value v at the front of list l and returns e.
func (l *List) PushFront(v interface{}) *Element { func (l *List) PushFront(v interface{}) *Element {
l.lazyInit() l.lazyInit()
return l.insertValue(v, &l.root) return l.insertValue(v, &l.root)
@ -178,6 +179,24 @@ func (l *List) MoveToBack(e *Element) {
l.insert(l.remove(e), l.root.prev) l.insert(l.remove(e), l.root.prev)
} }
// MoveBefore moves element e to its new position before mark.
// If e is not an element of l, or e == mark, the list is not modified.
func (l *List) MoveBefore(e, mark *Element) {
if e.list != l || e == mark {
return
}
l.insert(l.remove(e), mark.prev)
}
// MoveAfter moves element e to its new position after mark.
// If e is not an element of l, or e == mark, the list is not modified.
func (l *List) MoveAfter(e, mark *Element) {
if e.list != l || e == mark {
return
}
l.insert(l.remove(e), mark)
}
// PushBackList inserts a copy of an other list at the back of list l. // PushBackList inserts a copy of an other list at the back of list l.
// The lists l and other may be the same. // The lists l and other may be the same.
func (l *List) PushBackList(other *List) { func (l *List) PushBackList(other *List) {

View file

@ -233,3 +233,55 @@ func TestIssue4103(t *testing.T) {
t.Errorf("l1.Len() = %d, want 3", n) t.Errorf("l1.Len() = %d, want 3", n)
} }
} }
func TestIssue6349(t *testing.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
e := l.Front()
l.Remove(e)
if e.Value != 1 {
t.Errorf("e.value = %d, want 1", e.Value)
}
if e.Next() != nil {
t.Errorf("e.Next() != nil")
}
if e.Prev() != nil {
t.Errorf("e.Prev() != nil")
}
}
func TestMove(t *testing.T) {
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
e3 := l.PushBack(3)
e4 := l.PushBack(4)
l.MoveAfter(e3, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveAfter(e3, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e4)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e1, e2, e3, e4 = e1, e3, e2, e4
l.MoveBefore(e4, e1)
checkListPointers(t, l, []*Element{e4, e1, e2, e3})
e1, e2, e3, e4 = e4, e1, e2, e3
l.MoveAfter(e4, e1)
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
e1, e2, e3, e4 = e1, e4, e2, e3
l.MoveAfter(e2, e3)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e1, e2, e3, e4 = e1, e3, e2, e4
}

View file

@ -61,6 +61,13 @@ func (x *cbcEncrypter) CryptBlocks(dst, src []byte) {
} }
} }
func (x *cbcEncrypter) SetIV(iv []byte) {
if len(iv) != len(x.iv) {
panic("cipher: incorrect length IV")
}
copy(x.iv, iv)
}
type cbcDecrypter cbc type cbcDecrypter cbc
// NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining // NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining
@ -94,3 +101,10 @@ func (x *cbcDecrypter) CryptBlocks(dst, src []byte) {
dst = dst[x.blockSize:] dst = dst[x.blockSize:]
} }
} }
func (x *cbcDecrypter) SetIV(iv []byte) {
if len(iv) != len(x.iv) {
panic("cipher: incorrect length IV")
}
copy(x.iv, iv)
}

View file

@ -0,0 +1,350 @@
// Copyright 2013 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 cipher
import (
"crypto/subtle"
"errors"
)
// AEAD is a cipher mode providing authenticated encryption with associated
// data.
type AEAD interface {
// NonceSize returns the size of the nonce that must be passed to Seal
// and Open.
NonceSize() int
// Overhead returns the maximum difference between the lengths of a
// plaintext and ciphertext.
Overhead() int
// Seal encrypts and authenticates plaintext, authenticates the
// additional data and appends the result to dst, returning the updated
// slice. The nonce must be NonceSize() bytes long and unique for all
// time, for a given key.
//
// The plaintext and dst may alias exactly or not at all.
Seal(dst, nonce, plaintext, data []byte) []byte
// Open decrypts and authenticates ciphertext, authenticates the
// additional data and, if successful, appends the resulting plaintext
// to dst, returning the updated slice and true. On error, nil and
// false is returned. The nonce must be NonceSize() bytes long and both
// it and the additional data must match the value passed to Seal.
//
// The ciphertext and dst may alias exactly or not at all.
Open(dst, nonce, ciphertext, data []byte) ([]byte, error)
}
// gcmFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM
// standard and make getUint64 suitable for marshaling these values, the bits
// are stored backwards. For example:
// the coefficient of x⁰ can be obtained by v.low >> 63.
// the coefficient of x⁶³ can be obtained by v.low & 1.
// the coefficient of x⁶⁴ can be obtained by v.high >> 63.
// the coefficient of x¹²⁷ can be obtained by v.high & 1.
type gcmFieldElement struct {
low, high uint64
}
// gcm represents a Galois Counter Mode with a specific key. See
// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
type gcm struct {
cipher Block
// productTable contains the first sixteen powers of the key, H.
// However, they are in bit reversed order. See NewGCM.
productTable [16]gcmFieldElement
}
// NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode.
func NewGCM(cipher Block) (AEAD, error) {
if cipher.BlockSize() != gcmBlockSize {
return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
}
var key [gcmBlockSize]byte
cipher.Encrypt(key[:], key[:])
g := &gcm{cipher: cipher}
// We precompute 16 multiples of |key|. However, when we do lookups
// into this table we'll be using bits from a field element and
// therefore the bits will be in the reverse order. So normally one
// would expect, say, 4*key to be in index 4 of the table but due to
// this bit ordering it will actually be in index 0010 (base 2) = 2.
x := gcmFieldElement{
getUint64(key[:8]),
getUint64(key[8:]),
}
g.productTable[reverseBits(1)] = x
for i := 2; i < 16; i += 2 {
g.productTable[reverseBits(i)] = gcmDouble(&g.productTable[reverseBits(i/2)])
g.productTable[reverseBits(i+1)] = gcmAdd(&g.productTable[reverseBits(i)], &x)
}
return g, nil
}
const (
gcmBlockSize = 16
gcmTagSize = 16
gcmNonceSize = 12
)
func (*gcm) NonceSize() int {
return gcmNonceSize
}
func (*gcm) Overhead() int {
return gcmTagSize
}
func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte {
if len(nonce) != gcmNonceSize {
panic("cipher: incorrect nonce length given to GCM")
}
ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
// See GCM spec, section 7.1.
var counter, tagMask [gcmBlockSize]byte
copy(counter[:], nonce)
counter[gcmBlockSize-1] = 1
g.cipher.Encrypt(tagMask[:], counter[:])
gcmInc32(&counter)
g.counterCrypt(out, plaintext, &counter)
g.auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask)
return ret
}
var errOpen = errors.New("cipher: message authentication failed")
func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
if len(nonce) != gcmNonceSize {
panic("cipher: incorrect nonce length given to GCM")
}
if len(ciphertext) < gcmTagSize {
return nil, errOpen
}
tag := ciphertext[len(ciphertext)-gcmTagSize:]
ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
// See GCM spec, section 7.1.
var counter, tagMask [gcmBlockSize]byte
copy(counter[:], nonce)
counter[gcmBlockSize-1] = 1
g.cipher.Encrypt(tagMask[:], counter[:])
gcmInc32(&counter)
var expectedTag [gcmTagSize]byte
g.auth(expectedTag[:], ciphertext, data, &tagMask)
if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 {
return nil, errOpen
}
ret, out := sliceForAppend(dst, len(ciphertext))
g.counterCrypt(out, ciphertext, &counter)
return ret, nil
}
// reverseBits reverses the order of the bits of 4-bit number in i.
func reverseBits(i int) int {
i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
return i
}
// gcmAdd adds two elements of GF(2¹²⁸) and returns the sum.
func gcmAdd(x, y *gcmFieldElement) gcmFieldElement {
// Addition in a characteristic 2 field is just XOR.
return gcmFieldElement{x.low ^ y.low, x.high ^ y.high}
}
// gcmDouble returns the result of doubling an element of GF(2¹²⁸).
func gcmDouble(x *gcmFieldElement) (double gcmFieldElement) {
msbSet := x.high&1 == 1
// Because of the bit-ordering, doubling is actually a right shift.
double.high = x.high >> 1
double.high |= x.low << 63
double.low = x.low >> 1
// If the most-significant bit was set before shifting then it,
// conceptually, becomes a term of x^128. This is greater than the
// irreducible polynomial so the result has to be reduced. The
// irreducible polynomial is 1+x+x^2+x^7+x^128. We can subtract that to
// eliminate the term at x^128 which also means subtracting the other
// four terms. In characteristic 2 fields, subtraction == addition ==
// XOR.
if msbSet {
double.low ^= 0xe100000000000000
}
return
}
var gcmReductionTable = []uint16{
0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
}
// mul sets y to y*H, where H is the GCM key, fixed during NewGCM.
func (g *gcm) mul(y *gcmFieldElement) {
var z gcmFieldElement
for i := 0; i < 2; i++ {
word := y.high
if i == 1 {
word = y.low
}
// Multiplication works by multiplying z by 16 and adding in
// one of the precomputed multiples of H.
for j := 0; j < 64; j += 4 {
msw := z.high & 0xf
z.high >>= 4
z.high |= z.low << 60
z.low >>= 4
z.low ^= uint64(gcmReductionTable[msw]) << 48
// the values in |table| are ordered for
// little-endian bit positions. See the comment
// in NewGCM.
t := &g.productTable[word&0xf]
z.low ^= t.low
z.high ^= t.high
word >>= 4
}
}
*y = z
}
// updateBlocks extends y with more polynomial terms from blocks, based on
// Horner's rule. There must be a multiple of gcmBlockSize bytes in blocks.
func (g *gcm) updateBlocks(y *gcmFieldElement, blocks []byte) {
for len(blocks) > 0 {
y.low ^= getUint64(blocks)
y.high ^= getUint64(blocks[8:])
g.mul(y)
blocks = blocks[gcmBlockSize:]
}
}
// update extends y with more polynomial terms from data. If data is not a
// multiple of gcmBlockSize bytes long then the remainder is zero padded.
func (g *gcm) update(y *gcmFieldElement, data []byte) {
fullBlocks := (len(data) >> 4) << 4
g.updateBlocks(y, data[:fullBlocks])
if len(data) != fullBlocks {
var partialBlock [gcmBlockSize]byte
copy(partialBlock[:], data[fullBlocks:])
g.updateBlocks(y, partialBlock[:])
}
}
// gcmInc32 treats the final four bytes of counterBlock as a big-endian value
// and increments it.
func gcmInc32(counterBlock *[16]byte) {
c := 1
for i := gcmBlockSize - 1; i >= gcmBlockSize-4; i-- {
c += int(counterBlock[i])
counterBlock[i] = byte(c)
c >>= 8
}
}
// sliceForAppend takes a slice and a requested number of bytes. It returns a
// slice with the contents of the given slice followed by that many bytes and a
// second slice that aliases into it and contains only the extra bytes. If the
// original slice has sufficient capacity then no allocation is performed.
func sliceForAppend(in []byte, n int) (head, tail []byte) {
if total := len(in) + n; cap(in) >= total {
head = in[:total]
} else {
head = make([]byte, total)
copy(head, in)
}
tail = head[len(in):]
return
}
// counterCrypt crypts in to out using g.cipher in counter mode.
func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) {
var mask [gcmBlockSize]byte
for len(in) >= gcmBlockSize {
g.cipher.Encrypt(mask[:], counter[:])
gcmInc32(counter)
for i := range mask {
out[i] = in[i] ^ mask[i]
}
out = out[gcmBlockSize:]
in = in[gcmBlockSize:]
}
if len(in) > 0 {
g.cipher.Encrypt(mask[:], counter[:])
gcmInc32(counter)
for i := range in {
out[i] = in[i] ^ mask[i]
}
}
}
// auth calculates GHASH(ciphertext, additionalData), masks the result with
// tagMask and writes the result to out.
func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]byte) {
var y gcmFieldElement
g.update(&y, additionalData)
g.update(&y, ciphertext)
y.low ^= uint64(len(additionalData)) * 8
y.high ^= uint64(len(ciphertext)) * 8
g.mul(&y)
putUint64(out, y.low)
putUint64(out[8:], y.high)
for i := range tagMask {
out[i] ^= tagMask[i]
}
}
func getUint64(data []byte) uint64 {
r := uint64(data[0])<<56 |
uint64(data[1])<<48 |
uint64(data[2])<<40 |
uint64(data[3])<<32 |
uint64(data[4])<<24 |
uint64(data[5])<<16 |
uint64(data[6])<<8 |
uint64(data[7])
return r
}
func putUint64(out []byte, v uint64) {
out[0] = byte(v >> 56)
out[1] = byte(v >> 48)
out[2] = byte(v >> 40)
out[3] = byte(v >> 32)
out[4] = byte(v >> 24)
out[5] = byte(v >> 16)
out[6] = byte(v >> 8)
out[7] = byte(v)
}

View file

@ -0,0 +1,175 @@
// Copyright 2013 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 cipher_test
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"testing"
)
// AES-GCM test vectors taken from gcmEncryptExtIV128.rsp from
// http://csrc.nist.gov/groups/STM/cavp/index.html.
var aesGCMTests = []struct {
key, nonce, plaintext, ad, result string
}{
{
"11754cd72aec309bf52f7687212e8957",
"3c819d9a9bed087615030b65",
"",
"",
"250327c674aaf477aef2675748cf6971",
},
{
"ca47248ac0b6f8372a97ac43508308ed",
"ffd2b598feabc9019262d2be",
"",
"",
"60d20404af527d248d893ae495707d1a",
},
{
"77be63708971c4e240d1cb79e8d77feb",
"e0e00f19fed7ba0136a797f3",
"",
"7a43ec1d9c0a5a78a0b16533a6213cab",
"209fcc8d3675ed938e9c7166709dd946",
},
{
"7680c5d3ca6154758e510f4d25b98820",
"f8f105f9c3df4965780321f8",
"",
"c94c410194c765e3dcc7964379758ed3",
"94dca8edfcf90bb74b153c8d48a17930",
},
{
"7fddb57453c241d03efbed3ac44e371c",
"ee283a3fc75575e33efd4887",
"d5de42b461646c255c87bd2962d3b9a2",
"",
"2ccda4a5415cb91e135c2a0f78c9b2fdb36d1df9b9d5e596f83e8b7f52971cb3",
},
{
"ab72c77b97cb5fe9a382d9fe81ffdbed",
"54cc7dc2c37ec006bcc6d1da",
"007c5e5b3e59df24a7c355584fc1518d",
"",
"0e1bde206a07a9c2c1b65300f8c649972b4401346697138c7a4891ee59867d0c",
},
{
"fe47fcce5fc32665d2ae399e4eec72ba",
"5adb9609dbaeb58cbd6e7275",
"7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1b840382c4bccaf3bafb4ca8429bea063",
"88319d6e1d3ffa5f987199166c8a9b56c2aeba5a",
"98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf5393043736365253ddbc5db8778371495da76d269e5db3e291ef1982e4defedaa2249f898556b47",
},
{
"ec0c2ba17aa95cd6afffe949da9cc3a8",
"296bce5b50b7d66096d627ef",
"b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987b764b9611f6c0f8641843d5d58f3a242",
"f8d00f05d22bf68599bcdeb131292ad6e2df5d14",
"a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a07162995506fde6309ffc19e716eddf1a828c5a890147971946b627c40016da1ecf3e77",
},
{
"2c1f21cf0f6fb3661943155c3e3d8492",
"23cb5ff362e22426984d1907",
"42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d68b5615ba7c1220ff6510e259f06655d8",
"5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f4488f33cfb5e979e42b6e1cfc0a60238982a7aec",
"81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222b6ad57af43e1895df9dca2a5344a62cc57a3ee28136e94c74838997ae9823f3a",
},
{
"d9f7d2411091f947b4d6f1e2d1f0fb2e",
"e1934f5db57cc983e6b180e7",
"73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490c2c6f6166f4a59431e182663fcaea05a",
"0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a20115d2e51398344b16bee1ed7c499b353d6c597af8",
"aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d573c7891c2a91fbc48db29967ec9542b2321b51ca862cb637cdd03b99a0f93b134",
},
{
"fe9bb47deb3a61e423c2231841cfd1fb",
"4d328eb776f500a2f7fb47aa",
"f1cc3818e421876bb6b8bbd6c9",
"",
"b88c5c1977b35b517b0aeae96743fd4727fe5cdb4b5b42818dea7ef8c9",
},
{
"6703df3701a7f54911ca72e24dca046a",
"12823ab601c350ea4bc2488c",
"793cd125b0b84a043e3ac67717",
"",
"b2051c80014f42f08735a7b0cd38e6bcd29962e5f2c13626b85a877101",
},
}
func TestAESGCM(t *testing.T) {
for i, test := range aesGCMTests {
key, _ := hex.DecodeString(test.key)
aes, err := aes.NewCipher(key)
if err != nil {
t.Fatal(err)
}
nonce, _ := hex.DecodeString(test.nonce)
plaintext, _ := hex.DecodeString(test.plaintext)
ad, _ := hex.DecodeString(test.ad)
aesgcm, err := cipher.NewGCM(aes)
if err != nil {
t.Fatal(err)
}
ct := aesgcm.Seal(nil, nonce, plaintext, ad)
if ctHex := hex.EncodeToString(ct); ctHex != test.result {
t.Errorf("#%d: got %s, want %s", i, ctHex, test.result)
continue
}
plaintext2, err := aesgcm.Open(nil, nonce, ct, ad)
if err != nil {
t.Errorf("#%d: Open failed", i)
continue
}
if !bytes.Equal(plaintext, plaintext2) {
t.Errorf("#%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext)
continue
}
if len(ad) > 0 {
ad[0] ^= 0x80
if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil {
t.Errorf("#%d: Open was successful after altering additional data", i)
}
ad[0] ^= 0x80
}
nonce[0] ^= 0x80
if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil {
t.Errorf("#%d: Open was successful after altering nonce", i)
}
nonce[0] ^= 0x80
ct[0] ^= 0x80
if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil {
t.Errorf("#%d: Open was successful after altering ciphertext", i)
}
ct[0] ^= 0x80
}
}
func BenchmarkAESGCM(b *testing.B) {
buf := make([]byte, 1024)
b.SetBytes(int64(len(buf)))
var key [16]byte
var nonce [12]byte
aes, _ := aes.NewCipher(key[:])
aesgcm, _ := cipher.NewGCM(aes)
var out []byte
b.ResetTimer()
for i := 0; i < b.N; i++ {
out = aesgcm.Seal(out[:0], nonce[:], buf, nonce[:])
}
}

View file

@ -25,6 +25,8 @@ func (r StreamReader) Read(dst []byte) (n int, err error) {
// StreamWriter wraps a Stream into an io.Writer. It calls XORKeyStream // StreamWriter wraps a Stream into an io.Writer. It calls XORKeyStream
// to process each slice of data which passes through. If any Write call // to process each slice of data which passes through. If any Write call
// returns short then the StreamWriter is out of sync and must be discarded. // returns short then the StreamWriter is out of sync and must be discarded.
// A StreamWriter has no internal buffering; Close does not need
// to be called to flush write data.
type StreamWriter struct { type StreamWriter struct {
S Stream S Stream
W io.Writer W io.Writer
@ -43,8 +45,11 @@ func (w StreamWriter) Write(src []byte) (n int, err error) {
return return
} }
// Close closes the underlying Writer and returns its Close return value, if the Writer
// is also an io.Closer. Otherwise it returns nil.
func (w StreamWriter) Close() error { func (w StreamWriter) Close() error {
// This saves us from either requiring a WriteCloser or having a if c, ok := w.W.(io.Closer); ok {
// StreamWriterCloser. return c.Close()
return w.W.(io.Closer).Close() }
return nil
} }

View file

@ -7,6 +7,7 @@ package crypto
import ( import (
"hash" "hash"
"strconv"
) )
// Hash identifies a cryptographic hash function that is implemented in another // Hash identifies a cryptographic hash function that is implemented in another
@ -59,7 +60,7 @@ func (h Hash) New() hash.Hash {
return f() return f()
} }
} }
panic("crypto: requested hash function is unavailable") panic("crypto: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable")
} }
// Available reports whether the given hash function is linked into the binary. // Available reports whether the given hash function is linked into the binary.
@ -77,5 +78,8 @@ func RegisterHash(h Hash, f func() hash.Hash) {
hashes[h] = f hashes[h] = f
} }
// PublicKey represents a public key using an unspecified algorithm.
type PublicKey interface{}
// PrivateKey represents a private key using an unspecified algorithm. // PrivateKey represents a private key using an unspecified algorithm.
type PrivateKey interface{} type PrivateKey interface{}

View file

@ -10,7 +10,7 @@ import (
func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) { func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
b := binary.BigEndian.Uint64(src) b := binary.BigEndian.Uint64(src)
b = permuteBlock(b, initialPermutation[:]) b = permuteInitialBlock(b)
left, right := uint32(b>>32), uint32(b) left, right := uint32(b>>32), uint32(b)
var subkey uint64 var subkey uint64
@ -25,7 +25,7 @@ func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) {
} }
// switch left & right and perform final permutation // switch left & right and perform final permutation
preOutput := (uint64(right) << 32) | uint64(left) preOutput := (uint64(right) << 32) | uint64(left)
binary.BigEndian.PutUint64(dst, permuteBlock(preOutput, finalPermutation[:])) binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput))
} }
// Encrypt one block from src into dst, using the subkeys. // Encrypt one block from src into dst, using the subkeys.
@ -40,20 +40,24 @@ func decryptBlock(subkeys []uint64, dst, src []byte) {
// DES Feistel function // DES Feistel function
func feistel(right uint32, key uint64) (result uint32) { func feistel(right uint32, key uint64) (result uint32) {
sBoxLocations := key ^ permuteBlock(uint64(right), expansionFunction[:]) sBoxLocations := key ^ expandBlock(right)
var sBoxResult uint32 var sBoxResult uint32
for i := uint8(0); i < 8; i++ { for i := uint8(0); i < 8; i++ {
sBoxLocation := uint8(sBoxLocations>>42) & 0x3f sBoxLocation := uint8(sBoxLocations>>42) & 0x3f
sBoxLocations <<= 6 sBoxLocations <<= 6
// row determined by 1st and 6th bit // row determined by 1st and 6th bit
row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4)
// column is middle four bits // column is middle four bits
row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4)
column := (sBoxLocation >> 1) & 0xf column := (sBoxLocation >> 1) & 0xf
sBoxResult |= uint32(sBoxes[i][row][column]) << (4 * (7 - i)) sBoxResult ^= feistelBox[i][16*row+column]
} }
return uint32(permuteBlock(uint64(sBoxResult), permutationFunction[:])) return sBoxResult
} }
// feistelBox[s][16*i+j] contains the output of permutationFunction
// for sBoxes[s][i][j] << 4*(7-s)
var feistelBox [8][64]uint32
// general purpose function to perform DES block permutations // general purpose function to perform DES block permutations
func permuteBlock(src uint64, permutation []uint8) (block uint64) { func permuteBlock(src uint64, permutation []uint8) (block uint64) {
for position, n := range permutation { for position, n := range permutation {
@ -63,6 +67,127 @@ func permuteBlock(src uint64, permutation []uint8) (block uint64) {
return return
} }
func init() {
for s := range sBoxes {
for i := 0; i < 4; i++ {
for j := 0; j < 16; j++ {
f := uint64(sBoxes[s][i][j]) << (4 * (7 - uint(s)))
f = permuteBlock(uint64(f), permutationFunction[:])
feistelBox[s][16*i+j] = uint32(f)
}
}
}
}
// expandBlock expands an input block of 32 bits,
// producing an output block of 48 bits.
func expandBlock(src uint32) (block uint64) {
// rotate the 5 highest bits to the right.
src = (src << 5) | (src >> 27)
for i := 0; i < 8; i++ {
block <<= 6
// take the 6 bits on the right
block |= uint64(src) & (1<<6 - 1)
// advance by 4 bits.
src = (src << 4) | (src >> 28)
}
return
}
// permuteInitialBlock is equivalent to the permutation defined
// by initialPermutation.
func permuteInitialBlock(block uint64) uint64 {
// block = b7 b6 b5 b4 b3 b2 b1 b0 (8 bytes)
b1 := block >> 48
b2 := block << 48
block ^= b1 ^ b2 ^ b1<<48 ^ b2>>48
// block = b1 b0 b5 b4 b3 b2 b7 b6
b1 = block >> 32 & 0xff00ff
b2 = (block & 0xff00ff00)
block ^= b1<<32 ^ b2 ^ b1<<8 ^ b2<<24 // exchange b0 b4 with b3 b7
// block is now b1 b3 b5 b7 b0 b2 b4 b7, the permutation:
// ... 8
// ... 24
// ... 40
// ... 56
// 7 6 5 4 3 2 1 0
// 23 22 21 20 19 18 17 16
// ... 32
// ... 48
// exchange 4,5,6,7 with 32,33,34,35 etc.
b1 = block & 0x0f0f00000f0f0000
b2 = block & 0x0000f0f00000f0f0
block ^= b1 ^ b2 ^ b1>>12 ^ b2<<12
// block is the permutation:
//
// [+8] [+40]
//
// 7 6 5 4
// 23 22 21 20
// 3 2 1 0
// 19 18 17 16 [+32]
// exchange 0,1,4,5 with 18,19,22,23
b1 = block & 0x3300330033003300
b2 = block & 0x00cc00cc00cc00cc
block ^= b1 ^ b2 ^ b1>>6 ^ b2<<6
// block is the permutation:
// 15 14
// 13 12
// 11 10
// 9 8
// 7 6
// 5 4
// 3 2
// 1 0 [+16] [+32] [+64]
// exchange 0,2,4,6 with 9,11,13,15:
b1 = block & 0xaaaaaaaa55555555
block ^= b1 ^ b1>>33 ^ b1<<33
// block is the permutation:
// 6 14 22 30 38 46 54 62
// 4 12 20 28 36 44 52 60
// 2 10 18 26 34 42 50 58
// 0 8 16 24 32 40 48 56
// 7 15 23 31 39 47 55 63
// 5 13 21 29 37 45 53 61
// 3 11 19 27 35 43 51 59
// 1 9 17 25 33 41 49 57
return block
}
// permuteInitialBlock is equivalent to the permutation defined
// by finalPermutation.
func permuteFinalBlock(block uint64) uint64 {
// Perform the same bit exchanges as permuteInitialBlock
// but in reverse order.
b1 := block & 0xaaaaaaaa55555555
block ^= b1 ^ b1>>33 ^ b1<<33
b1 = block & 0x3300330033003300
b2 := block & 0x00cc00cc00cc00cc
block ^= b1 ^ b2 ^ b1>>6 ^ b2<<6
b1 = block & 0x0f0f00000f0f0000
b2 = block & 0x0000f0f00000f0f0
block ^= b1 ^ b2 ^ b1>>12 ^ b2<<12
b1 = block >> 32 & 0xff00ff
b2 = (block & 0xff00ff00)
block ^= b1<<32 ^ b2 ^ b1<<8 ^ b2<<24
b1 = block >> 48
b2 = block << 48
block ^= b1 ^ b2 ^ b1<<48 ^ b2>>48
return block
}
// creates 16 28-bit blocks rotated according // creates 16 28-bit blocks rotated according
// to the rotation schedule // to the rotation schedule
func ksRotate(in uint32) (out []uint32) { func ksRotate(in uint32) (out []uint32) {

View file

@ -1504,20 +1504,63 @@ func TestSubstitutionTableKnownAnswerDecrypt(t *testing.T) {
} }
} }
func ExampleNewTripleDESCipher() { func TestInitialPermute(t *testing.T) {
// NewTripleDESCipher can also be used when EDE2 is required by for i := uint(0); i < 64; i++ {
// duplicating the first 8 bytes of the 16-byte key. bit := uint64(1) << i
ede2Key := []byte("example key 1234") got := permuteInitialBlock(bit)
want := uint64(1) << finalPermutation[63-i]
var tripleDESKey []byte if got != want {
tripleDESKey = append(tripleDESKey, ede2Key[:16]...) t.Errorf("permute(%x) = %x, want %x", bit, got, want)
tripleDESKey = append(tripleDESKey, ede2Key[:8]...) }
}
_, err := NewTripleDESCipher(tripleDESKey) }
if err != nil {
panic(err) func TestFinalPermute(t *testing.T) {
for i := uint(0); i < 64; i++ {
bit := uint64(1) << i
got := permuteFinalBlock(bit)
want := uint64(1) << initialPermutation[63-i]
if got != want {
t.Errorf("permute(%x) = %x, want %x", bit, got, want)
}
}
}
func TestExpandBlock(t *testing.T) {
for i := uint(0); i < 32; i++ {
bit := uint32(1) << i
got := expandBlock(bit)
want := permuteBlock(uint64(bit), expansionFunction[:])
if got != want {
t.Errorf("expand(%x) = %x, want %x", bit, got, want)
}
}
}
func BenchmarkEncrypt(b *testing.B) {
tt := encryptDESTests[0]
c, err := NewCipher(tt.key)
if err != nil {
b.Fatal("NewCipher:", err)
}
out := make([]byte, len(tt.in))
b.SetBytes(int64(len(out)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.Encrypt(out, tt.in)
}
}
func BenchmarkDecrypt(b *testing.B) {
tt := encryptDESTests[0]
c, err := NewCipher(tt.key)
if err != nil {
b.Fatal("NewCipher:", err)
}
out := make([]byte, len(tt.out))
b.SetBytes(int64(len(out)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
c.Decrypt(out, tt.out)
} }
// See crypto/cipher for how to use a cipher.Block for encryption and
// decryption.
} }

View file

@ -123,8 +123,8 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
return return
} }
// Verify verifies the signature in r, s of hash using the public key, pub. It // Verify verifies the signature in r, s of hash using the public key, pub. Its
// returns true iff the signature is valid. // return value records whether the signature is valid.
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
// See [NSA] 3.4.2 // See [NSA] 3.4.2
c := pub.Curve c := pub.Curve

View file

@ -322,7 +322,6 @@ func Unmarshal(curve Curve, data []byte) (x, y *big.Int) {
} }
var initonce sync.Once var initonce sync.Once
var p256 *CurveParams
var p384 *CurveParams var p384 *CurveParams
var p521 *CurveParams var p521 *CurveParams
@ -333,17 +332,6 @@ func initAll() {
initP521() initP521()
} }
func initP256() {
// See FIPS 186-3, section D.2.3
p256 = new(CurveParams)
p256.P, _ = new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10)
p256.N, _ = new(big.Int).SetString("115792089210356248762697446949407573529996955224135760342422259061068512044369", 10)
p256.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)
p256.Gx, _ = new(big.Int).SetString("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16)
p256.Gy, _ = new(big.Int).SetString("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16)
p256.BitSize = 256
}
func initP384() { func initP384() {
// See FIPS 186-3, section D.2.4 // See FIPS 186-3, section D.2.4
p384 = new(CurveParams) p384 = new(CurveParams)

View file

@ -322,6 +322,52 @@ func TestGenericBaseMult(t *testing.T) {
} }
} }
func TestP256BaseMult(t *testing.T) {
p256 := P256()
p256Generic := p256.Params()
scalars := make([]*big.Int, 0, len(p224BaseMultTests)+1)
for _, e := range p224BaseMultTests {
k, _ := new(big.Int).SetString(e.k, 10)
scalars = append(scalars, k)
}
k := new(big.Int).SetInt64(1)
k.Lsh(k, 500)
scalars = append(scalars, k)
for i, k := range scalars {
x, y := p256.ScalarBaseMult(k.Bytes())
x2, y2 := p256Generic.ScalarBaseMult(k.Bytes())
if x.Cmp(x2) != 0 || y.Cmp(y2) != 0 {
t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, x, y, x2, y2)
}
if testing.Short() && i > 5 {
break
}
}
}
func TestP256Mult(t *testing.T) {
p256 := P256()
p256Generic := p256.Params()
for i, e := range p224BaseMultTests {
x, _ := new(big.Int).SetString(e.x, 16)
y, _ := new(big.Int).SetString(e.y, 16)
k, _ := new(big.Int).SetString(e.k, 10)
xx, yy := p256.ScalarMult(x, y, k.Bytes())
xx2, yy2 := p256Generic.ScalarMult(x, y, k.Bytes())
if xx.Cmp(xx2) != 0 || yy.Cmp(yy2) != 0 {
t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, xx, yy, xx2, yy2)
}
if testing.Short() && i > 5 {
break
}
}
}
func TestInfinity(t *testing.T) { func TestInfinity(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@ -371,6 +417,17 @@ func BenchmarkBaseMult(b *testing.B) {
} }
} }
func BenchmarkBaseMultP256(b *testing.B) {
b.ResetTimer()
p256 := P256()
e := p224BaseMultTests[25]
k, _ := new(big.Int).SetString(e.k, 10)
b.StartTimer()
for i := 0; i < b.N; i++ {
p256.ScalarBaseMult(k.Bytes())
}
}
func TestMarshal(t *testing.T) { func TestMarshal(t *testing.T) {
p224 := P224() p224 := P224()
_, x, y, err := GenerateKey(p224, rand.Reader) _, x, y, err := GenerateKey(p224, rand.Reader)

File diff suppressed because it is too large Load diff

View file

@ -164,7 +164,7 @@ var program = `
// DO NOT EDIT. // DO NOT EDIT.
// Generate with: go run gen.go{{if .Full}} -full{{end}} | gofmt >md5block.go // Generate with: go run gen.go{{if .Full}} -full{{end}} | gofmt >md5block.go
// +build !amd64 // +build !amd64,!386,!arm
package md5 package md5

View file

@ -88,7 +88,11 @@ func (d *digest) Write(p []byte) (nn int, err error) {
func (d0 *digest) Sum(in []byte) []byte { func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing. // Make a copy of d0 so that caller can keep writing and summing.
d := *d0 d := *d0
hash := d.checkSum()
return append(in, hash[:]...)
}
func (d *digest) checkSum() [Size]byte {
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
len := d.len len := d.len
var tmp [64]byte var tmp [64]byte
@ -118,5 +122,13 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*4+3] = byte(s >> 24) digest[i*4+3] = byte(s >> 24)
} }
return append(in, digest[:]...) return digest
}
// Sum returns the MD5 checksum of the data.
func Sum(data []byte) [Size]byte {
var d digest
d.Reset()
d.Write(data)
return d.checkSum()
} }

View file

@ -53,6 +53,10 @@ var golden = []md5Test{
func TestGolden(t *testing.T) { func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ { for i := 0; i < len(golden); i++ {
g := golden[i] g := golden[i]
s := fmt.Sprintf("%x", Sum([]byte(g.in)))
if s != g.out {
t.Fatalf("Sum function: md5(%s) = %s want %s", g.in, s, g.out)
}
c := New() c := New()
buf := make([]byte, len(g.in)+4) buf := make([]byte, len(g.in)+4)
for j := 0; j < 3+4; j++ { for j := 0; j < 3+4; j++ {
@ -77,12 +81,28 @@ func TestGolden(t *testing.T) {
} }
} }
func ExampleNew() { func TestLarge(t *testing.T) {
h := New() const N = 10000
io.WriteString(h, "The fog is getting thicker!") ok := "2bb571599a4180e1d542f76904adc3df" // md5sum of "0123456789" * 1000
io.WriteString(h, "And Leon's getting laaarger!") block := make([]byte, 10004)
fmt.Printf("%x", h.Sum(nil)) c := New()
// Output: e2c569be17396eca2a2e3c11578123ed for offset := 0; offset < 4; offset++ {
for i := 0; i < N; i++ {
block[offset+i] = '0' + byte(i%10)
}
for blockSize := 10; blockSize <= N; blockSize *= 10 {
blocks := N / blockSize
b := block[offset : offset+blockSize]
c.Reset()
for i := 0; i < blocks; i++ {
c.Write(b)
}
s := fmt.Sprintf("%x", c.Sum(nil))
if s != ok {
t.Fatalf("md5 TestLarge offset=%d, blockSize=%d = %s want %s", offset, blockSize, s, ok)
}
}
}
} }
var bench = New() var bench = New()

View file

@ -1,7 +1,7 @@
// DO NOT EDIT. // DO NOT EDIT.
// Generate with: go run gen.go -full | gofmt >md5block.go // Generate with: go run gen.go -full | gofmt >md5block.go
// +build !amd64,!386 // +build !amd64,!386,!arm
package md5 package md5

View file

@ -2,8 +2,10 @@
// 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 amd64 386 // +build amd64 386 arm
package md5 package md5
//go:noescape
func block(dig *digest, p []byte) func block(dig *digest, p []byte)

View file

@ -14,5 +14,8 @@ import "io"
// On Windows systems, Reader uses the CryptGenRandom API. // On Windows systems, Reader uses the CryptGenRandom API.
var Reader io.Reader var Reader io.Reader
// Read is a helper function that calls Reader.Read. // Read is a helper function that calls Reader.Read using io.ReadFull.
func Read(b []byte) (n int, err error) { return Reader.Read(b) } // On return, n == len(b) if and only if err == nil.
func Read(b []byte) (n int, err error) {
return io.ReadFull(Reader, b)
}

View file

@ -2,7 +2,7 @@
// 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 netbsd openbsd plan9 // +build darwin dragonfly freebsd linux netbsd openbsd plan9
// Unix cryptographically secure pseudorandom number // Unix cryptographically secure pseudorandom number
// generator. // generator.

View file

@ -124,7 +124,11 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid
lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
} }
valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) // The PS padding must be at least 8 bytes long, and it starts two
// bytes into em.
validPS := subtle.ConstantTimeLessOrEq(2+8, index)
valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) & validPS
msg = em[index+1:] msg = em[index+1:]
return return
} }

View file

@ -197,6 +197,14 @@ func TestVerifyPKCS1v15(t *testing.T) {
} }
} }
func TestOverlongMessagePKCS1v15(t *testing.T) {
ciphertext := decodeBase64("fjOVdirUzFoLlukv80dBllMLjXythIf22feqPrNo0YoIjzyzyoMFiLjAc/Y4krkeZ11XFThIrEvw\nkRiZcCq5ng==")
_, err := DecryptPKCS1v15(nil, rsaPrivateKey, ciphertext)
if err == nil {
t.Error("RSA decrypted a message that was too long.")
}
}
// In order to generate new test vectors you'll need the PEM form of this key: // In order to generate new test vectors you'll need the PEM form of this key:
// -----BEGIN RSA PRIVATE KEY----- // -----BEGIN RSA PRIVATE KEY-----
// MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 // MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0

282
libgo/go/crypto/rsa/pss.go Normal file
View file

@ -0,0 +1,282 @@
// Copyright 2013 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 rsa
// This file implementes the PSS signature scheme [1].
//
// [1] http://www.rsa.com/rsalabs/pkcs/files/h11300-wp-pkcs-1v2-2-rsa-cryptography-standard.pdf
import (
"bytes"
"crypto"
"errors"
"hash"
"io"
"math/big"
)
func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) {
// See [1], section 9.1.1
hLen := hash.Size()
sLen := len(salt)
emLen := (emBits + 7) / 8
// 1. If the length of M is greater than the input limitation for the
// hash function (2^61 - 1 octets for SHA-1), output "message too
// long" and stop.
//
// 2. Let mHash = Hash(M), an octet string of length hLen.
if len(mHash) != hLen {
return nil, errors.New("crypto/rsa: input must be hashed message")
}
// 3. If emLen < hLen + sLen + 2, output "encoding error" and stop.
if emLen < hLen+sLen+2 {
return nil, errors.New("crypto/rsa: encoding error")
}
em := make([]byte, emLen)
db := em[:emLen-sLen-hLen-2+1+sLen]
h := em[emLen-sLen-hLen-2+1+sLen : emLen-1]
// 4. Generate a random octet string salt of length sLen; if sLen = 0,
// then salt is the empty string.
//
// 5. Let
// M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt;
//
// M' is an octet string of length 8 + hLen + sLen with eight
// initial zero octets.
//
// 6. Let H = Hash(M'), an octet string of length hLen.
var prefix [8]byte
hash.Write(prefix[:])
hash.Write(mHash)
hash.Write(salt)
h = hash.Sum(h[:0])
hash.Reset()
// 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2
// zero octets. The length of PS may be 0.
//
// 8. Let DB = PS || 0x01 || salt; DB is an octet string of length
// emLen - hLen - 1.
db[emLen-sLen-hLen-2] = 0x01
copy(db[emLen-sLen-hLen-1:], salt)
// 9. Let dbMask = MGF(H, emLen - hLen - 1).
//
// 10. Let maskedDB = DB \xor dbMask.
mgf1XOR(db, hash, h)
// 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in
// maskedDB to zero.
db[0] &= (0xFF >> uint(8*emLen-emBits))
// 12. Let EM = maskedDB || H || 0xbc.
em[emLen-1] = 0xBC
// 13. Output EM.
return em, nil
}
func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error {
// 1. If the length of M is greater than the input limitation for the
// hash function (2^61 - 1 octets for SHA-1), output "inconsistent"
// and stop.
//
// 2. Let mHash = Hash(M), an octet string of length hLen.
hLen := hash.Size()
if hLen != len(mHash) {
return ErrVerification
}
// 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop.
emLen := (emBits + 7) / 8
if emLen < hLen+sLen+2 {
return ErrVerification
}
// 4. If the rightmost octet of EM does not have hexadecimal value
// 0xbc, output "inconsistent" and stop.
if em[len(em)-1] != 0xBC {
return ErrVerification
}
// 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and
// let H be the next hLen octets.
db := em[:emLen-hLen-1]
h := em[emLen-hLen-1 : len(em)-1]
// 6. If the leftmost 8 * emLen - emBits bits of the leftmost octet in
// maskedDB are not all equal to zero, output "inconsistent" and
// stop.
if em[0]&(0xFF<<uint(8-(8*emLen-emBits))) != 0 {
return ErrVerification
}
// 7. Let dbMask = MGF(H, emLen - hLen - 1).
//
// 8. Let DB = maskedDB \xor dbMask.
mgf1XOR(db, hash, h)
// 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB
// to zero.
db[0] &= (0xFF >> uint(8*emLen-emBits))
if sLen == PSSSaltLengthAuto {
FindSaltLength:
for sLen = emLen - (hLen + 2); sLen >= 0; sLen-- {
switch db[emLen-hLen-sLen-2] {
case 1:
break FindSaltLength
case 0:
continue
default:
return ErrVerification
}
}
if sLen < 0 {
return ErrVerification
}
} else {
// 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero
// or if the octet at position emLen - hLen - sLen - 1 (the leftmost
// position is "position 1") does not have hexadecimal value 0x01,
// output "inconsistent" and stop.
for _, e := range db[:emLen-hLen-sLen-2] {
if e != 0x00 {
return ErrVerification
}
}
if db[emLen-hLen-sLen-2] != 0x01 {
return ErrVerification
}
}
// 11. Let salt be the last sLen octets of DB.
salt := db[len(db)-sLen:]
// 12. Let
// M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
// M' is an octet string of length 8 + hLen + sLen with eight
// initial zero octets.
//
// 13. Let H' = Hash(M'), an octet string of length hLen.
var prefix [8]byte
hash.Write(prefix[:])
hash.Write(mHash)
hash.Write(salt)
h0 := hash.Sum(nil)
// 14. If H = H', output "consistent." Otherwise, output "inconsistent."
if !bytes.Equal(h0, h) {
return ErrVerification
}
return nil
}
// signPSSWithSalt calculates the signature of hashed using PSS [1] with specified salt.
// Note that hashed must be the result of hashing the input message using the
// given hash funcion. salt is a random sequence of bytes whose length will be
// later used to verify the signature.
func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error) {
nBits := priv.N.BitLen()
em, err := emsaPSSEncode(hashed, nBits-1, salt, hash.New())
if err != nil {
return
}
m := new(big.Int).SetBytes(em)
c, err := decrypt(rand, priv, m)
if err != nil {
return
}
s = make([]byte, (nBits+7)/8)
copyWithLeftPad(s, c.Bytes())
return
}
const (
// PSSSaltLengthAuto causes the salt in a PSS signature to be as large
// as possible when signing, and to be auto-detected when verifying.
PSSSaltLengthAuto = 0
// PSSSaltLengthEqualsHash causes the salt length to equal the length
// of the hash used in the signature.
PSSSaltLengthEqualsHash = -1
)
// PSSOptions contains options for creating and verifying PSS signatures.
type PSSOptions struct {
// SaltLength controls the length of the salt used in the PSS
// signature. It can either be a number of bytes, or one of the special
// PSSSaltLength constants.
SaltLength int
}
func (opts *PSSOptions) saltLength() int {
if opts == nil {
return PSSSaltLengthAuto
}
return opts.SaltLength
}
// SignPSS calculates the signature of hashed using RSASSA-PSS [1].
// Note that hashed must be the result of hashing the input message using the
// given hash funcion. The opts argument may be nil, in which case sensible
// defaults are used.
func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) (s []byte, err error) {
saltLength := opts.saltLength()
switch saltLength {
case PSSSaltLengthAuto:
saltLength = (priv.N.BitLen()+7)/8 - 2 - hash.Size()
case PSSSaltLengthEqualsHash:
saltLength = hash.Size()
}
salt := make([]byte, saltLength)
if _, err = io.ReadFull(rand, salt); err != nil {
return
}
return signPSSWithSalt(rand, priv, hash, hashed, salt)
}
// VerifyPSS verifies a PSS signature.
// hashed is the result of hashing the input message using the given hash
// function and sig is the signature. A valid signature is indicated by
// returning a nil error. The opts argument may be nil, in which case sensible
// defaults are used.
func VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts *PSSOptions) error {
return verifyPSS(pub, hash, hashed, sig, opts.saltLength())
}
// verifyPSS verifies a PSS signature with the given salt length.
func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error {
nBits := pub.N.BitLen()
if len(sig) != (nBits+7)/8 {
return ErrVerification
}
s := new(big.Int).SetBytes(sig)
m := encrypt(new(big.Int), pub, s)
emBits := nBits - 1
emLen := (emBits + 7) / 8
if emLen < len(m.Bytes()) {
return ErrVerification
}
em := make([]byte, emLen)
copyWithLeftPad(em, m.Bytes())
if saltLen == PSSSaltLengthEqualsHash {
saltLen = hash.Size()
}
return emsaPSSVerify(hashed, em, emBits, saltLen, hash.New())
}

View file

@ -0,0 +1,249 @@
// Copyright 2013 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 rsa
import (
"bufio"
"bytes"
"compress/bzip2"
"crypto"
_ "crypto/md5"
"crypto/rand"
"crypto/sha1"
_ "crypto/sha256"
"encoding/hex"
"math/big"
"os"
"strconv"
"strings"
"testing"
)
func TestEMSAPSS(t *testing.T) {
// Test vector in file pss-int.txt from: ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
msg := []byte{
0x85, 0x9e, 0xef, 0x2f, 0xd7, 0x8a, 0xca, 0x00, 0x30, 0x8b,
0xdc, 0x47, 0x11, 0x93, 0xbf, 0x55, 0xbf, 0x9d, 0x78, 0xdb,
0x8f, 0x8a, 0x67, 0x2b, 0x48, 0x46, 0x34, 0xf3, 0xc9, 0xc2,
0x6e, 0x64, 0x78, 0xae, 0x10, 0x26, 0x0f, 0xe0, 0xdd, 0x8c,
0x08, 0x2e, 0x53, 0xa5, 0x29, 0x3a, 0xf2, 0x17, 0x3c, 0xd5,
0x0c, 0x6d, 0x5d, 0x35, 0x4f, 0xeb, 0xf7, 0x8b, 0x26, 0x02,
0x1c, 0x25, 0xc0, 0x27, 0x12, 0xe7, 0x8c, 0xd4, 0x69, 0x4c,
0x9f, 0x46, 0x97, 0x77, 0xe4, 0x51, 0xe7, 0xf8, 0xe9, 0xe0,
0x4c, 0xd3, 0x73, 0x9c, 0x6b, 0xbf, 0xed, 0xae, 0x48, 0x7f,
0xb5, 0x56, 0x44, 0xe9, 0xca, 0x74, 0xff, 0x77, 0xa5, 0x3c,
0xb7, 0x29, 0x80, 0x2f, 0x6e, 0xd4, 0xa5, 0xff, 0xa8, 0xba,
0x15, 0x98, 0x90, 0xfc,
}
salt := []byte{
0xe3, 0xb5, 0xd5, 0xd0, 0x02, 0xc1, 0xbc, 0xe5, 0x0c, 0x2b,
0x65, 0xef, 0x88, 0xa1, 0x88, 0xd8, 0x3b, 0xce, 0x7e, 0x61,
}
expected := []byte{
0x66, 0xe4, 0x67, 0x2e, 0x83, 0x6a, 0xd1, 0x21, 0xba, 0x24,
0x4b, 0xed, 0x65, 0x76, 0xb8, 0x67, 0xd9, 0xa4, 0x47, 0xc2,
0x8a, 0x6e, 0x66, 0xa5, 0xb8, 0x7d, 0xee, 0x7f, 0xbc, 0x7e,
0x65, 0xaf, 0x50, 0x57, 0xf8, 0x6f, 0xae, 0x89, 0x84, 0xd9,
0xba, 0x7f, 0x96, 0x9a, 0xd6, 0xfe, 0x02, 0xa4, 0xd7, 0x5f,
0x74, 0x45, 0xfe, 0xfd, 0xd8, 0x5b, 0x6d, 0x3a, 0x47, 0x7c,
0x28, 0xd2, 0x4b, 0xa1, 0xe3, 0x75, 0x6f, 0x79, 0x2d, 0xd1,
0xdc, 0xe8, 0xca, 0x94, 0x44, 0x0e, 0xcb, 0x52, 0x79, 0xec,
0xd3, 0x18, 0x3a, 0x31, 0x1f, 0xc8, 0x96, 0xda, 0x1c, 0xb3,
0x93, 0x11, 0xaf, 0x37, 0xea, 0x4a, 0x75, 0xe2, 0x4b, 0xdb,
0xfd, 0x5c, 0x1d, 0xa0, 0xde, 0x7c, 0xec, 0xdf, 0x1a, 0x89,
0x6f, 0x9d, 0x8b, 0xc8, 0x16, 0xd9, 0x7c, 0xd7, 0xa2, 0xc4,
0x3b, 0xad, 0x54, 0x6f, 0xbe, 0x8c, 0xfe, 0xbc,
}
hash := sha1.New()
hash.Write(msg)
hashed := hash.Sum(nil)
encoded, err := emsaPSSEncode(hashed, 1023, salt, sha1.New())
if err != nil {
t.Errorf("Error from emsaPSSEncode: %s\n", err)
}
if !bytes.Equal(encoded, expected) {
t.Errorf("Bad encoding. got %x, want %x", encoded, expected)
}
if err = emsaPSSVerify(hashed, encoded, 1023, len(salt), sha1.New()); err != nil {
t.Errorf("Bad verification: %s", err)
}
}
// TestPSSGolden tests all the test vectors in pss-vect.txt from
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
func TestPSSGolden(t *testing.T) {
inFile, err := os.Open("testdata/pss-vect.txt.bz2")
if err != nil {
t.Fatalf("Failed to open input file: %s", err)
}
defer inFile.Close()
// The pss-vect.txt file contains RSA keys and then a series of
// signatures. A goroutine is used to preprocess the input by merging
// lines, removing spaces in hex values and identifying the start of
// new keys and signature blocks.
const newKeyMarker = "START NEW KEY"
const newSignatureMarker = "START NEW SIGNATURE"
values := make(chan string)
go func() {
defer close(values)
scanner := bufio.NewScanner(bzip2.NewReader(inFile))
var partialValue string
lastWasValue := true
for scanner.Scan() {
line := scanner.Text()
switch {
case len(line) == 0:
if len(partialValue) > 0 {
values <- strings.Replace(partialValue, " ", "", -1)
partialValue = ""
lastWasValue = true
}
continue
case strings.HasPrefix(line, "# ======") && lastWasValue:
values <- newKeyMarker
lastWasValue = false
case strings.HasPrefix(line, "# ------") && lastWasValue:
values <- newSignatureMarker
lastWasValue = false
case strings.HasPrefix(line, "#"):
continue
default:
partialValue += line
}
}
if err := scanner.Err(); err != nil {
panic(err)
}
}()
var key *PublicKey
var hashed []byte
hash := crypto.SHA1
h := hash.New()
opts := &PSSOptions{
SaltLength: PSSSaltLengthEqualsHash,
}
for marker := range values {
switch marker {
case newKeyMarker:
key = new(PublicKey)
nHex, ok := <-values
if !ok {
continue
}
key.N = bigFromHex(nHex)
key.E = intFromHex(<-values)
// We don't care for d, p, q, dP, dQ or qInv.
for i := 0; i < 6; i++ {
<-values
}
case newSignatureMarker:
msg := fromHex(<-values)
<-values // skip salt
sig := fromHex(<-values)
h.Reset()
h.Write(msg)
hashed = h.Sum(hashed[:0])
if err := VerifyPSS(key, hash, hashed, sig, opts); err != nil {
t.Error(err)
}
default:
t.Fatalf("unknown marker: " + marker)
}
}
}
// TestPSSOpenSSL ensures that we can verify a PSS signature from OpenSSL with
// the default options. OpenSSL sets the salt length to be maximal.
func TestPSSOpenSSL(t *testing.T) {
hash := crypto.SHA256
h := hash.New()
h.Write([]byte("testing"))
hashed := h.Sum(nil)
// Generated with `echo -n testing | openssl dgst -sign key.pem -sigopt rsa_padding_mode:pss -sha256 > sig`
sig := []byte{
0x95, 0x59, 0x6f, 0xd3, 0x10, 0xa2, 0xe7, 0xa2, 0x92, 0x9d,
0x4a, 0x07, 0x2e, 0x2b, 0x27, 0xcc, 0x06, 0xc2, 0x87, 0x2c,
0x52, 0xf0, 0x4a, 0xcc, 0x05, 0x94, 0xf2, 0xc3, 0x2e, 0x20,
0xd7, 0x3e, 0x66, 0x62, 0xb5, 0x95, 0x2b, 0xa3, 0x93, 0x9a,
0x66, 0x64, 0x25, 0xe0, 0x74, 0x66, 0x8c, 0x3e, 0x92, 0xeb,
0xc6, 0xe6, 0xc0, 0x44, 0xf3, 0xb4, 0xb4, 0x2e, 0x8c, 0x66,
0x0a, 0x37, 0x9c, 0x69,
}
if err := VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, nil); err != nil {
t.Error(err)
}
}
func TestPSSSigning(t *testing.T) {
var saltLengthCombinations = []struct {
signSaltLength, verifySaltLength int
good bool
}{
{PSSSaltLengthAuto, PSSSaltLengthAuto, true},
{PSSSaltLengthEqualsHash, PSSSaltLengthAuto, true},
{PSSSaltLengthEqualsHash, PSSSaltLengthEqualsHash, true},
{PSSSaltLengthEqualsHash, 8, false},
{PSSSaltLengthAuto, PSSSaltLengthEqualsHash, false},
{8, 8, true},
}
hash := crypto.MD5
h := hash.New()
h.Write([]byte("testing"))
hashed := h.Sum(nil)
var opts PSSOptions
for i, test := range saltLengthCombinations {
opts.SaltLength = test.signSaltLength
sig, err := SignPSS(rand.Reader, rsaPrivateKey, hash, hashed, &opts)
if err != nil {
t.Errorf("#%d: error while signing: %s", i, err)
continue
}
opts.SaltLength = test.verifySaltLength
err = VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, &opts)
if (err == nil) != test.good {
t.Errorf("#%d: bad result, wanted: %t, got: %s", i, test.good, err)
}
}
}
func bigFromHex(hex string) *big.Int {
n, ok := new(big.Int).SetString(hex, 16)
if !ok {
panic("bad hex: " + hex)
}
return n
}
func intFromHex(hex string) int {
i, err := strconv.ParseInt(hex, 16, 32)
if err != nil {
panic(err)
}
return int(i)
}
func fromHex(hexStr string) []byte {
s, err := hex.DecodeString(hexStr)
if err != nil {
panic(err)
}
return s
}

View file

@ -5,8 +5,6 @@
// Package rsa implements RSA encryption as specified in PKCS#1. // Package rsa implements RSA encryption as specified in PKCS#1.
package rsa package rsa
// TODO(agl): Add support for PSS padding.
import ( import (
"crypto/rand" "crypto/rand"
"crypto/subtle" "crypto/subtle"

View file

@ -120,8 +120,10 @@ func testKeyBasics(t *testing.T, priv *PrivateKey) {
} }
func fromBase10(base10 string) *big.Int { func fromBase10(base10 string) *big.Int {
i := new(big.Int) i, ok := new(big.Int).SetString(base10, 10)
i.SetString(base10, 10) if !ok {
panic("bad number: " + base10)
}
return i return i
} }

Binary file not shown.

View file

@ -90,9 +90,13 @@ func (d *digest) Write(p []byte) (nn int, err error) {
func (d0 *digest) Sum(in []byte) []byte { func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing. // Make a copy of d0 so that caller can keep writing and summing.
d := *d0 d := *d0
hash := d.checkSum()
return append(in, hash[:]...)
}
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. func (d *digest) checkSum() [Size]byte {
len := d.len len := d.len
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
var tmp [64]byte var tmp [64]byte
tmp[0] = 0x80 tmp[0] = 0x80
if len%64 < 56 { if len%64 < 56 {
@ -120,5 +124,13 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*4+3] = byte(s) digest[i*4+3] = byte(s)
} }
return append(in, digest[:]...) return digest
}
// Sum returns the SHA1 checksum of the data.
func Sum(data []byte) [Size]byte {
var d digest
d.Reset()
d.Write(data)
return d.checkSum()
} }

View file

@ -54,6 +54,10 @@ var golden = []sha1Test{
func TestGolden(t *testing.T) { func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ { for i := 0; i < len(golden); i++ {
g := golden[i] g := golden[i]
s := fmt.Sprintf("%x", Sum([]byte(g.in)))
if s != g.out {
t.Fatalf("Sum function: sha1(%s) = %s want %s", g.in, s, g.out)
}
c := New() c := New()
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
if j < 2 { if j < 2 {
@ -72,13 +76,6 @@ func TestGolden(t *testing.T) {
} }
} }
func ExampleNew() {
h := New()
io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.")
fmt.Printf("% x", h.Sum(nil))
// Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd
}
var bench = New() var bench = New()
var buf = make([]byte, 8192) var buf = make([]byte, 8192)

View file

@ -6,4 +6,6 @@
package sha1 package sha1
//go:noescape
func block(dig *digest, p []byte) func block(dig *digest, p []byte)

View file

@ -134,9 +134,16 @@ func (d *digest) Write(p []byte) (nn int, err error) {
func (d0 *digest) Sum(in []byte) []byte { func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing. // Make a copy of d0 so that caller can keep writing and summing.
d := *d0 d := *d0
hash := d.checkSum()
if d.is224 {
return append(in, hash[:Size224]...)
}
return append(in, hash[:]...)
}
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. func (d *digest) checkSum() [Size]byte {
len := d.len len := d.len
// Padding. Add a 1 bit and 0 bits until 56 bytes mod 64.
var tmp [64]byte var tmp [64]byte
tmp[0] = 0x80 tmp[0] = 0x80
if len%64 < 56 { if len%64 < 56 {
@ -157,10 +164,8 @@ func (d0 *digest) Sum(in []byte) []byte {
} }
h := d.h[:] h := d.h[:]
size := Size
if d.is224 { if d.is224 {
h = d.h[:7] h = d.h[:7]
size = Size224
} }
var digest [Size]byte var digest [Size]byte
@ -171,5 +176,24 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*4+3] = byte(s) digest[i*4+3] = byte(s)
} }
return append(in, digest[:size]...) return digest
}
// Sum256 returns the SHA256 checksum of the data.
func Sum256(data []byte) [Size]byte {
var d digest
d.Reset()
d.Write(data)
return d.checkSum()
}
// Sum224 returns the SHA224 checksum of the data.
func Sum224(data []byte) (sum224 [Size224]byte) {
var d digest
d.is224 = true
d.Reset()
d.Write(data)
sum := d.checkSum()
copy(sum224[:], sum[:Size224])
return
} }

View file

@ -88,6 +88,10 @@ var golden224 = []sha256Test{
func TestGolden(t *testing.T) { func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ { for i := 0; i < len(golden); i++ {
g := golden[i] g := golden[i]
s := fmt.Sprintf("%x", Sum256([]byte(g.in)))
if s != g.out {
t.Fatalf("Sum256 function: sha256(%s) = %s want %s", g.in, s, g.out)
}
c := New() c := New()
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
if j < 2 { if j < 2 {
@ -106,6 +110,10 @@ func TestGolden(t *testing.T) {
} }
for i := 0; i < len(golden224); i++ { for i := 0; i < len(golden224); i++ {
g := golden224[i] g := golden224[i]
s := fmt.Sprintf("%x", Sum224([]byte(g.in)))
if s != g.out {
t.Fatalf("Sum224 function: sha224(%s) = %s want %s", g.in, s, g.out)
}
c := New224() c := New224()
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
if j < 2 { if j < 2 {

View file

@ -135,7 +135,14 @@ func (d0 *digest) Sum(in []byte) []byte {
// Make a copy of d0 so that caller can keep writing and summing. // Make a copy of d0 so that caller can keep writing and summing.
d := new(digest) d := new(digest)
*d = *d0 *d = *d0
hash := d.checkSum()
if d.is384 {
return append(in, hash[:Size384]...)
}
return append(in, hash[:]...)
}
func (d *digest) checkSum() [Size]byte {
// Padding. Add a 1 bit and 0 bits until 112 bytes mod 128. // Padding. Add a 1 bit and 0 bits until 112 bytes mod 128.
len := d.len len := d.len
var tmp [128]byte var tmp [128]byte
@ -158,10 +165,8 @@ func (d0 *digest) Sum(in []byte) []byte {
} }
h := d.h[:] h := d.h[:]
size := Size
if d.is384 { if d.is384 {
h = d.h[:6] h = d.h[:6]
size = Size384
} }
var digest [Size]byte var digest [Size]byte
@ -176,5 +181,24 @@ func (d0 *digest) Sum(in []byte) []byte {
digest[i*8+7] = byte(s) digest[i*8+7] = byte(s)
} }
return append(in, digest[:size]...) return digest
}
// Sum512 returns the SHA512 checksum of the data.
func Sum512(data []byte) [Size]byte {
var d digest
d.Reset()
d.Write(data)
return d.checkSum()
}
// Sum384 returns the SHA384 checksum of the data.
func Sum384(data []byte) (sum384 [Size384]byte) {
var d digest
d.is384 = true
d.Reset()
d.Write(data)
sum := d.checkSum()
copy(sum384[:], sum[:Size384])
return
} }

View file

@ -88,6 +88,10 @@ var golden384 = []sha512Test{
func TestGolden(t *testing.T) { func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ { for i := 0; i < len(golden); i++ {
g := golden[i] g := golden[i]
s := fmt.Sprintf("%x", Sum512([]byte(g.in)))
if s != g.out {
t.Fatalf("Sum512 function: sha512(%s) = %s want %s", g.in, s, g.out)
}
c := New() c := New()
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
if j < 2 { if j < 2 {
@ -106,6 +110,10 @@ func TestGolden(t *testing.T) {
} }
for i := 0; i < len(golden384); i++ { for i := 0; i < len(golden384); i++ {
g := golden384[i] g := golden384[i]
s := fmt.Sprintf("%x", Sum384([]byte(g.in)))
if s != g.out {
t.Fatalf("Sum384 function: sha384(%s) = %s want %s", g.in, s, g.out)
}
c := New384() c := New384()
for j := 0; j < 3; j++ { for j := 0; j < 3; j++ {
if j < 2 { if j < 2 {

View file

@ -55,3 +55,11 @@ func ConstantTimeCopy(v int, x, y []byte) {
} }
return return
} }
// ConstantTimeLessOrEq returns 1 if x <= y and 0 otherwise.
// Its behavior is undefined if x or y are negative or > 2**31 - 1.
func ConstantTimeLessOrEq(x, y int) int {
x32 := int32(x)
y32 := int32(y)
return int(((x32 - y32 - 1) >> 31) & 1)
}

View file

@ -103,3 +103,23 @@ func TestConstantTimeCopy(t *testing.T) {
t.Error(err) t.Error(err)
} }
} }
var lessOrEqTests = []struct {
x, y, result int
}{
{0, 0, 1},
{1, 0, 0},
{0, 1, 1},
{10, 20, 1},
{20, 10, 0},
{10, 10, 1},
}
func TestConstantTimeLessOrEq(t *testing.T) {
for i, test := range lessOrEqTests {
result := ConstantTimeLessOrEq(test.x, test.y)
if result != test.result {
t.Errorf("#%d: %d <= %d gave %d, expected %d", i, test.x, test.y, result, test.result)
}
}
}

View file

@ -34,6 +34,22 @@ type keyAgreement interface {
generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
} }
const (
// suiteECDH indicates that the cipher suite involves elliptic curve
// Diffie-Hellman. This means that it should only be selected when the
// client indicates that it supports ECC with a curve and point format
// that we're happy with.
suiteECDHE = 1 << iota
// suiteECDSA indicates that the cipher suite involves an ECDSA
// signature and therefore may only be selected when the server's
// certificate is ECDSA. If this is not set then the cipher suite is
// RSA based.
suiteECDSA
// suiteTLS12 indicates that the cipher suite should only be advertised
// and accepted when using TLS 1.2.
suiteTLS12
)
// A cipherSuite is a specific combination of key agreement, cipher and MAC // A cipherSuite is a specific combination of key agreement, cipher and MAC
// function. All cipher suites currently assume RSA key agreement. // function. All cipher suites currently assume RSA key agreement.
type cipherSuite struct { type cipherSuite struct {
@ -42,24 +58,30 @@ type cipherSuite struct {
keyLen int keyLen int
macLen int macLen int
ivLen int ivLen int
ka func() keyAgreement ka func(version uint16) keyAgreement
// If elliptic is set, a server will only consider this ciphersuite if // flags is a bitmask of the suite* values, above.
// the ClientHello indicated that the client supports an elliptic curve flags int
// and point format that we can handle. cipher func(key, iv []byte, isRead bool) interface{}
elliptic bool mac func(version uint16, macKey []byte) macFunction
cipher func(key, iv []byte, isRead bool) interface{} aead func(key, fixedNonce []byte) cipher.AEAD
mac func(version uint16, macKey []byte) macFunction
} }
var cipherSuites = []*cipherSuite{ var cipherSuites = []*cipherSuite{
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1}, // Ciphersuite order is chosen so that ECDHE comes before plain RSA
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1}, // and RC4 comes before AES (because of the Lucky13 attack).
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1}, {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, false, cipherAES, macSHA1}, {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1}, {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1}, {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherRC4, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
} }
func cipherRC4(key, iv []byte, isRead bool) interface{} { func cipherRC4(key, iv []byte, isRead bool) interface{} {
@ -85,7 +107,7 @@ func cipherAES(key, iv []byte, isRead bool) interface{} {
// macSHA1 returns a macFunction for the given protocol version. // macSHA1 returns a macFunction for the given protocol version.
func macSHA1(version uint16, key []byte) macFunction { func macSHA1(version uint16, key []byte) macFunction {
if version == versionSSL30 { if version == VersionSSL30 {
mac := ssl30MAC{ mac := ssl30MAC{
h: sha1.New(), h: sha1.New(),
key: make([]byte, len(key)), key: make([]byte, len(key)),
@ -98,7 +120,47 @@ func macSHA1(version uint16, key []byte) macFunction {
type macFunction interface { type macFunction interface {
Size() int Size() int
MAC(digestBuf, seq, data []byte) []byte MAC(digestBuf, seq, header, data []byte) []byte
}
// fixedNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
// each call.
type fixedNonceAEAD struct {
// sealNonce and openNonce are buffers where the larger nonce will be
// constructed. Since a seal and open operation may be running
// concurrently, there is a separate buffer for each.
sealNonce, openNonce []byte
aead cipher.AEAD
}
func (f *fixedNonceAEAD) NonceSize() int { return 8 }
func (f *fixedNonceAEAD) Overhead() int { return f.aead.Overhead() }
func (f *fixedNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
copy(f.sealNonce[len(f.sealNonce)-8:], nonce)
return f.aead.Seal(out, f.sealNonce, plaintext, additionalData)
}
func (f *fixedNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) {
copy(f.openNonce[len(f.openNonce)-8:], nonce)
return f.aead.Open(out, f.openNonce, plaintext, additionalData)
}
func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD {
aes, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
aead, err := cipher.NewGCM(aes)
if err != nil {
panic(err)
}
nonce1, nonce2 := make([]byte, 12), make([]byte, 12)
copy(nonce1, fixedNonce)
copy(nonce2, fixedNonce)
return &fixedNonceAEAD{nonce1, nonce2, aead}
} }
// ssl30MAC implements the SSLv3 MAC function, as defined in // ssl30MAC implements the SSLv3 MAC function, as defined in
@ -116,7 +178,7 @@ var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0
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} 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(digestBuf, seq, record []byte) []byte { func (s ssl30MAC) MAC(digestBuf, seq, header, data []byte) []byte {
padLength := 48 padLength := 48
if s.h.Size() == 20 { if s.h.Size() == 20 {
padLength = 40 padLength = 40
@ -126,9 +188,9 @@ func (s ssl30MAC) MAC(digestBuf, seq, record []byte) []byte {
s.h.Write(s.key) s.h.Write(s.key)
s.h.Write(ssl30Pad1[:padLength]) s.h.Write(ssl30Pad1[:padLength])
s.h.Write(seq) s.h.Write(seq)
s.h.Write(record[:1]) s.h.Write(header[:1])
s.h.Write(record[3:5]) s.h.Write(header[3:5])
s.h.Write(record[recordHeaderLen:]) s.h.Write(data)
digestBuf = s.h.Sum(digestBuf[:0]) digestBuf = s.h.Sum(digestBuf[:0])
s.h.Reset() s.h.Reset()
@ -147,19 +209,30 @@ func (s tls10MAC) Size() int {
return s.h.Size() return s.h.Size()
} }
func (s tls10MAC) MAC(digestBuf, seq, record []byte) []byte { func (s tls10MAC) MAC(digestBuf, seq, header, data []byte) []byte {
s.h.Reset() s.h.Reset()
s.h.Write(seq) s.h.Write(seq)
s.h.Write(record) s.h.Write(header)
s.h.Write(data)
return s.h.Sum(digestBuf[:0]) return s.h.Sum(digestBuf[:0])
} }
func rsaKA() keyAgreement { func rsaKA(version uint16) keyAgreement {
return rsaKeyAgreement{} return rsaKeyAgreement{}
} }
func ecdheRSAKA() keyAgreement { func ecdheECDSAKA(version uint16) keyAgreement {
return new(ecdheRSAKeyAgreement) return &ecdheKeyAgreement{
sigType: signatureECDSA,
version: version,
}
}
func ecdheRSAKA(version uint16) keyAgreement {
return &ecdheKeyAgreement{
sigType: signatureRSA,
version: version,
}
} }
// mutualCipherSuite returns a cipherSuite given a list of supported // mutualCipherSuite returns a cipherSuite given a list of supported
@ -181,12 +254,17 @@ func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
// 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_3DES_EDE_CBC_SHA uint16 = 0x000a TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014 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
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
) )

View file

@ -9,22 +9,27 @@ import (
"crypto/rand" "crypto/rand"
"crypto/x509" "crypto/x509"
"io" "io"
"math/big"
"strings" "strings"
"sync" "sync"
"time" "time"
) )
const (
VersionSSL30 = 0x0300
VersionTLS10 = 0x0301
VersionTLS11 = 0x0302
VersionTLS12 = 0x0303
)
const ( const (
maxPlaintext = 16384 // maximum plaintext payload length maxPlaintext = 16384 // maximum plaintext payload length
maxCiphertext = 16384 + 2048 // maximum ciphertext payload length maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
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)
versionSSL30 = 0x0300 minVersion = VersionSSL30
versionTLS10 = 0x0301 maxVersion = VersionTLS12
minVersion = versionSSL30
maxVersion = versionTLS10
) )
// TLS record types. // TLS record types.
@ -60,12 +65,13 @@ const (
// TLS extension numbers // TLS extension numbers
var ( var (
extensionServerName uint16 = 0 extensionServerName uint16 = 0
extensionStatusRequest uint16 = 5 extensionStatusRequest uint16 = 5
extensionSupportedCurves uint16 = 10 extensionSupportedCurves uint16 = 10
extensionSupportedPoints uint16 = 11 extensionSupportedPoints uint16 = 11
extensionSessionTicket uint16 = 35 extensionSignatureAlgorithms uint16 = 13
extensionNextProtoNeg uint16 = 13172 // not IANA assigned extensionSessionTicket uint16 = 35
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
) )
// TLS Elliptic Curves // TLS Elliptic Curves
@ -93,25 +99,60 @@ const (
certTypeDSSSign = 2 // A certificate containing a DSA key certTypeDSSSign = 2 // A certificate containing a DSA key
certTypeRSAFixedDH = 3 // A certificate containing a static DH key certTypeRSAFixedDH = 3 // A certificate containing a static DH key
certTypeDSSFixedDH = 4 // A certificate containing a static DH key certTypeDSSFixedDH = 4 // A certificate containing a static DH key
// See RFC4492 sections 3 and 5.5.
certTypeECDSASign = 64 // A certificate containing an ECDSA-capable public key, signed with ECDSA.
certTypeRSAFixedECDH = 65 // A certificate containing an ECDH-capable public key, signed with RSA.
certTypeECDSAFixedECDH = 66 // A certificate containing an ECDH-capable public key, signed with ECDSA.
// Rest of these are reserved by the TLS spec // Rest of these are reserved by the TLS spec
) )
// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1)
const (
hashSHA1 uint8 = 2
hashSHA256 uint8 = 4
)
// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1)
const (
signatureRSA uint8 = 1
signatureECDSA uint8 = 3
)
// signatureAndHash mirrors the TLS 1.2, SignatureAndHashAlgorithm struct. See
// RFC 5246, section A.4.1.
type signatureAndHash struct {
hash, signature uint8
}
// supportedSKXSignatureAlgorithms contains the signature and hash algorithms
// that the code advertises as supported in a TLS 1.2 ClientHello.
var supportedSKXSignatureAlgorithms = []signatureAndHash{
{hashSHA256, signatureRSA},
{hashSHA256, signatureECDSA},
{hashSHA1, signatureRSA},
{hashSHA1, signatureECDSA},
}
// supportedClientCertSignatureAlgorithms contains the signature and hash
// algorithms that the code advertises as supported in a TLS 1.2
// CertificateRequest.
var supportedClientCertSignatureAlgorithms = []signatureAndHash{
{hashSHA256, signatureRSA},
{hashSHA256, signatureECDSA},
}
// ConnectionState records basic TLS details about the connection. // ConnectionState records basic TLS details about the connection.
type ConnectionState struct { type ConnectionState struct {
HandshakeComplete bool HandshakeComplete bool // TLS handshake is complete
DidResume bool DidResume bool // connection resumes a previous TLS connection
CipherSuite uint16 CipherSuite uint16 // cipher suite in use (TLS_RSA_WITH_RC4_128_SHA, ...)
NegotiatedProtocol string NegotiatedProtocol string // negotiated next protocol (from Config.NextProtos)
NegotiatedProtocolIsMutual bool NegotiatedProtocolIsMutual bool // negotiated protocol was advertised by server
ServerName string // server name requested by client, if any (server side only)
// ServerName contains the server name indicated by the client, if any. PeerCertificates []*x509.Certificate // certificate chain presented by remote peer
// (Only valid for server connections.) VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
ServerName string
// the certificate chain that was presented by the other side
PeerCertificates []*x509.Certificate
// the verified certificate chains built from PeerCertificates.
VerifiedChains [][]*x509.Certificate
} }
// ClientAuthType declares the policy the server will follow for // ClientAuthType declares the policy the server will follow for
@ -204,6 +245,15 @@ type Config struct {
// connections using that key are compromised. // connections using that key are compromised.
SessionTicketKey [32]byte SessionTicketKey [32]byte
// MinVersion contains the minimum SSL/TLS version that is acceptable.
// If zero, then SSLv3 is taken as the minimum.
MinVersion uint16
// MaxVersion contains the maximum SSL/TLS version that is acceptable.
// If zero, then the maximum version supported by this package is used,
// which is currently TLS 1.2.
MaxVersion uint16
serverInitOnce sync.Once // guards calling (*Config).serverInit serverInitOnce sync.Once // guards calling (*Config).serverInit
} }
@ -248,6 +298,35 @@ func (c *Config) cipherSuites() []uint16 {
return s return s
} }
func (c *Config) minVersion() uint16 {
if c == nil || c.MinVersion == 0 {
return minVersion
}
return c.MinVersion
}
func (c *Config) maxVersion() uint16 {
if c == nil || c.MaxVersion == 0 {
return maxVersion
}
return c.MaxVersion
}
// mutualVersion returns the protocol version to use given the advertised
// version of the peer.
func (c *Config) mutualVersion(vers uint16) (uint16, bool) {
minVersion := c.minVersion()
maxVersion := c.maxVersion()
if vers < minVersion {
return 0, false
}
if vers > maxVersion {
vers = maxVersion
}
return vers, true
}
// getCertificateForName returns the best certificate for the given name, // getCertificateForName returns the best certificate for the given name,
// defaulting to the first element of c.Certificates if there are no good // defaulting to the first element of c.Certificates if there are no good
// options. // options.
@ -304,7 +383,7 @@ func (c *Config) BuildNameToCertificate() {
// 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
PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey, *ecdsa.PrivateKey
// OCSPStaple contains an optional OCSP response which will be served // OCSPStaple contains an optional OCSP response which will be served
// to clients that request it. // to clients that request it.
OCSPStaple []byte OCSPStaple []byte
@ -327,18 +406,13 @@ type handshakeMessage interface {
unmarshal([]byte) bool unmarshal([]byte) bool
} }
// mutualVersion returns the protocol version to use given the advertised // TODO(jsing): Make these available to both crypto/x509 and crypto/tls.
// version of the peer. type dsaSignature struct {
func mutualVersion(vers uint16) (uint16, bool) { R, S *big.Int
if vers < minVersion {
return 0, false
}
if vers > maxVersion {
vers = maxVersion
}
return vers, true
} }
type ecdsaSignature dsaSignature
var emptyConfig Config var emptyConfig Config
func defaultConfig() *Config { func defaultConfig() *Config {

View file

@ -146,6 +146,9 @@ func (hc *halfConn) changeCipherSpec() error {
hc.mac = hc.nextMac hc.mac = hc.nextMac
hc.nextCipher = nil hc.nextCipher = nil
hc.nextMac = nil hc.nextMac = nil
for i := range hc.seq {
hc.seq[i] = 0
}
return nil return nil
} }
@ -229,8 +232,16 @@ func roundUp(a, b int) int {
return a + (b-a%b)%b return a + (b-a%b)%b
} }
// decrypt checks and strips the mac and decrypts the data in b. // cbcMode is an interface for block ciphers using cipher block chaining.
func (hc *halfConn) decrypt(b *block) (bool, alert) { type cbcMode interface {
cipher.BlockMode
SetIV([]byte)
}
// decrypt checks and strips the mac and decrypts the data in b. Returns a
// success boolean, the number of bytes to skip from the start of the record in
// order to get the application payload, and an optional alert value.
func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) {
// pull out payload // pull out payload
payload := b.data[recordHeaderLen:] payload := b.data[recordHeaderLen:]
@ -240,26 +251,54 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) {
} }
paddingGood := byte(255) paddingGood := byte(255)
explicitIVLen := 0
// decrypt // decrypt
if hc.cipher != nil { if hc.cipher != nil {
switch c := hc.cipher.(type) { switch c := hc.cipher.(type) {
case cipher.Stream: case cipher.Stream:
c.XORKeyStream(payload, payload) c.XORKeyStream(payload, payload)
case cipher.BlockMode: case cipher.AEAD:
blockSize := c.BlockSize() explicitIVLen = 8
if len(payload) < explicitIVLen {
return false, 0, alertBadRecordMAC
}
nonce := payload[:8]
payload = payload[8:]
if len(payload)%blockSize != 0 || len(payload) < roundUp(macSize+1, blockSize) { var additionalData [13]byte
return false, alertBadRecordMAC copy(additionalData[:], hc.seq[:])
copy(additionalData[8:], b.data[:3])
n := len(payload) - c.Overhead()
additionalData[11] = byte(n >> 8)
additionalData[12] = byte(n)
var err error
payload, err = c.Open(payload[:0], nonce, payload, additionalData[:])
if err != nil {
return false, 0, alertBadRecordMAC
}
b.resize(recordHeaderLen + explicitIVLen + len(payload))
case cbcMode:
blockSize := c.BlockSize()
if hc.version >= VersionTLS11 {
explicitIVLen = blockSize
} }
if len(payload)%blockSize != 0 || len(payload) < roundUp(explicitIVLen+macSize+1, blockSize) {
return false, 0, alertBadRecordMAC
}
if explicitIVLen > 0 {
c.SetIV(payload[:explicitIVLen])
payload = payload[explicitIVLen:]
}
c.CryptBlocks(payload, payload) c.CryptBlocks(payload, payload)
if hc.version == versionSSL30 { if hc.version == VersionSSL30 {
payload, paddingGood = removePaddingSSL30(payload) payload, paddingGood = removePaddingSSL30(payload)
} else { } else {
payload, paddingGood = removePadding(payload) payload, paddingGood = removePadding(payload)
} }
b.resize(recordHeaderLen + len(payload)) b.resize(recordHeaderLen + explicitIVLen + len(payload))
// note that we still have a timing side-channel in the // note that we still have a timing side-channel in the
// MAC check, below. An attacker can align the record // MAC check, below. An attacker can align the record
@ -279,25 +318,25 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) {
// check, strip mac // check, strip mac
if hc.mac != nil { if hc.mac != nil {
if len(payload) < macSize { if len(payload) < macSize {
return false, alertBadRecordMAC return false, 0, alertBadRecordMAC
} }
// strip mac off payload, b.data // strip mac off payload, b.data
n := len(payload) - macSize n := len(payload) - macSize
b.data[3] = byte(n >> 8) b.data[3] = byte(n >> 8)
b.data[4] = byte(n) b.data[4] = byte(n)
b.resize(recordHeaderLen + n) b.resize(recordHeaderLen + explicitIVLen + n)
remoteMAC := payload[n:] remoteMAC := payload[n:]
localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data) localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n])
hc.incSeq()
if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 {
return false, alertBadRecordMAC return false, 0, alertBadRecordMAC
} }
hc.inDigestBuf = localMAC hc.inDigestBuf = localMAC
} }
hc.incSeq()
return true, 0 return true, recordHeaderLen + explicitIVLen, 0
} }
// padToBlockSize calculates the needed padding block, if any, for a payload. // padToBlockSize calculates the needed padding block, if any, for a payload.
@ -318,11 +357,10 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) {
} }
// encrypt encrypts and macs the data in b. // encrypt encrypts and macs the data in b.
func (hc *halfConn) encrypt(b *block) (bool, alert) { func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
// mac // mac
if hc.mac != nil { if hc.mac != nil {
mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data) mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:])
hc.incSeq()
n := len(b.data) n := len(b.data)
b.resize(n + len(mac)) b.resize(n + len(mac))
@ -337,11 +375,30 @@ func (hc *halfConn) encrypt(b *block) (bool, alert) {
switch c := hc.cipher.(type) { switch c := hc.cipher.(type) {
case cipher.Stream: case cipher.Stream:
c.XORKeyStream(payload, payload) c.XORKeyStream(payload, payload)
case cipher.BlockMode: case cipher.AEAD:
prefix, finalBlock := padToBlockSize(payload, c.BlockSize()) payloadLen := len(b.data) - recordHeaderLen - explicitIVLen
b.resize(recordHeaderLen + len(prefix) + len(finalBlock)) b.resize(len(b.data) + c.Overhead())
c.CryptBlocks(b.data[recordHeaderLen:], prefix) nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
c.CryptBlocks(b.data[recordHeaderLen+len(prefix):], finalBlock) payload := b.data[recordHeaderLen+explicitIVLen:]
payload = payload[:payloadLen]
var additionalData [13]byte
copy(additionalData[:], hc.seq[:])
copy(additionalData[8:], b.data[:3])
additionalData[11] = byte(payloadLen >> 8)
additionalData[12] = byte(payloadLen)
c.Seal(payload[:0], nonce, payload, additionalData[:])
case cbcMode:
blockSize := c.BlockSize()
if explicitIVLen > 0 {
c.SetIV(payload[:explicitIVLen])
payload = payload[explicitIVLen:]
}
prefix, finalBlock := padToBlockSize(payload, blockSize)
b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock))
c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix)
c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock)
default: default:
panic("unknown cipher type") panic("unknown cipher type")
} }
@ -351,6 +408,7 @@ func (hc *halfConn) encrypt(b *block) (bool, alert) {
n := len(b.data) - recordHeaderLen n := len(b.data) - recordHeaderLen
b.data[3] = byte(n >> 8) b.data[3] = byte(n >> 8)
b.data[4] = byte(n) b.data[4] = byte(n)
hc.incSeq()
return true, 0 return true, 0
} }
@ -534,10 +592,11 @@ Again:
// Process message. // Process message.
b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
b.off = recordHeaderLen ok, off, err := c.in.decrypt(b)
if ok, err := c.in.decrypt(b); !ok { if !ok {
return c.sendAlert(err) return c.sendAlert(err)
} }
b.off = off
data := b.data[b.off:] data := b.data[b.off:]
if len(data) > maxPlaintext { if len(data) > maxPlaintext {
c.sendAlert(alertRecordOverflow) c.sendAlert(alertRecordOverflow)
@ -637,18 +696,52 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
if m > maxPlaintext { if m > maxPlaintext {
m = maxPlaintext m = maxPlaintext
} }
b.resize(recordHeaderLen + m) explicitIVLen := 0
explicitIVIsSeq := false
var cbc cbcMode
if c.out.version >= VersionTLS11 {
var ok bool
if cbc, ok = c.out.cipher.(cbcMode); ok {
explicitIVLen = cbc.BlockSize()
}
}
if explicitIVLen == 0 {
if _, ok := c.out.cipher.(cipher.AEAD); ok {
explicitIVLen = 8
// The AES-GCM construction in TLS has an
// explicit nonce so that the nonce can be
// random. However, the nonce is only 8 bytes
// which is too small for a secure, random
// nonce. Therefore we use the sequence number
// as the nonce.
explicitIVIsSeq = true
}
}
b.resize(recordHeaderLen + explicitIVLen + m)
b.data[0] = byte(typ) b.data[0] = byte(typ)
vers := c.vers vers := c.vers
if vers == 0 { if vers == 0 {
vers = maxVersion // Some TLS servers fail if the record version is
// greater than TLS 1.0 for the initial ClientHello.
vers = VersionTLS10
} }
b.data[1] = byte(vers >> 8) b.data[1] = byte(vers >> 8)
b.data[2] = byte(vers) b.data[2] = byte(vers)
b.data[3] = byte(m >> 8) b.data[3] = byte(m >> 8)
b.data[4] = byte(m) b.data[4] = byte(m)
copy(b.data[recordHeaderLen:], data) if explicitIVLen > 0 {
c.out.encrypt(b) explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
if explicitIVIsSeq {
copy(explicitIV, c.out.seq[:])
} else {
if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil {
break
}
}
}
copy(b.data[recordHeaderLen+explicitIVLen:], data)
c.out.encrypt(b, explicitIVLen)
_, err = c.conn.Write(b.data) _, err = c.conn.Write(b.data)
if err != nil { if err != nil {
break break
@ -709,7 +802,9 @@ func (c *Conn) readHandshake() (interface{}, error) {
case typeCertificate: case typeCertificate:
m = new(certificateMsg) m = new(certificateMsg)
case typeCertificateRequest: case typeCertificateRequest:
m = new(certificateRequestMsg) m = &certificateRequestMsg{
hasSignatureAndHash: c.vers >= VersionTLS12,
}
case typeCertificateStatus: case typeCertificateStatus:
m = new(certificateStatusMsg) m = new(certificateStatusMsg)
case typeServerKeyExchange: case typeServerKeyExchange:
@ -719,7 +814,9 @@ func (c *Conn) readHandshake() (interface{}, error) {
case typeClientKeyExchange: case typeClientKeyExchange:
m = new(clientKeyExchangeMsg) m = new(clientKeyExchangeMsg)
case typeCertificateVerify: case typeCertificateVerify:
m = new(certificateVerifyMsg) m = &certificateVerifyMsg{
hasSignatureAndHash: c.vers >= VersionTLS12,
}
case typeNextProtocol: case typeNextProtocol:
m = new(nextProtoMsg) m = new(nextProtoMsg)
case typeFinished: case typeFinished:
@ -768,7 +865,7 @@ func (c *Conn) Write(b []byte) (int, error) {
// http://www.imperialviolet.org/2012/01/15/beastfollowup.html // http://www.imperialviolet.org/2012/01/15/beastfollowup.html
var m int var m int
if len(b) > 1 && c.vers <= versionTLS10 { if len(b) > 1 && c.vers <= VersionTLS10 {
if _, ok := c.out.cipher.(cipher.BlockMode); ok { if _, ok := c.out.cipher.(cipher.BlockMode); ok {
n, err := c.writeRecord(recordTypeApplicationData, b[:1]) n, err := c.writeRecord(recordTypeApplicationData, b[:1])
if err != nil { if err != nil {
@ -792,21 +889,32 @@ func (c *Conn) Read(b []byte) (n int, err error) {
c.in.Lock() c.in.Lock()
defer c.in.Unlock() defer c.in.Unlock()
for c.input == nil && c.error() == nil { // Some OpenSSL servers send empty records in order to randomize the
if err := c.readRecord(recordTypeApplicationData); err != nil { // CBC IV. So this loop ignores a limited number of empty records.
// Soft error, like EAGAIN const maxConsecutiveEmptyRecords = 100
for emptyRecordCount := 0; emptyRecordCount <= maxConsecutiveEmptyRecords; emptyRecordCount++ {
for c.input == nil && c.error() == nil {
if err := c.readRecord(recordTypeApplicationData); err != nil {
// Soft error, like EAGAIN
return 0, err
}
}
if err := c.error(); err != nil {
return 0, err return 0, err
} }
n, err = c.input.Read(b)
if c.input.off >= len(c.input.data) {
c.in.freeBlock(c.input)
c.input = nil
}
if n != 0 || err != nil {
return n, err
}
} }
if err := c.error(); err != nil {
return 0, err return 0, io.ErrNoProgress
}
n, err = c.input.Read(b)
if c.input.off >= len(c.input.data) {
c.in.freeBlock(c.input)
c.input = nil
}
return n, nil
} }
// Close closes the connection. // Close closes the connection.

View file

@ -30,7 +30,7 @@ var (
validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011") validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for") validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority") isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
rsaBits = flag.Int("rsa-bits", 1024, "Size of RSA key to generate") rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate")
) )
func main() { func main() {

View file

@ -6,25 +6,23 @@ package tls
import ( import (
"bytes" "bytes"
"crypto" "crypto/ecdsa"
"crypto/rsa" "crypto/rsa"
"crypto/subtle" "crypto/subtle"
"crypto/x509" "crypto/x509"
"encoding/asn1"
"errors" "errors"
"io" "io"
"strconv" "strconv"
) )
func (c *Conn) clientHandshake() error { func (c *Conn) clientHandshake() error {
finishedHash := newFinishedHash(versionTLS10)
if c.config == nil { if c.config == nil {
c.config = defaultConfig() c.config = defaultConfig()
} }
hello := &clientHelloMsg{ hello := &clientHelloMsg{
vers: maxVersion, vers: c.config.maxVersion(),
cipherSuites: c.config.cipherSuites(),
compressionMethods: []uint8{compressionNone}, compressionMethods: []uint8{compressionNone},
random: make([]byte, 32), random: make([]byte, 32),
ocspStapling: true, ocspStapling: true,
@ -34,6 +32,25 @@ func (c *Conn) clientHandshake() error {
nextProtoNeg: len(c.config.NextProtos) > 0, nextProtoNeg: len(c.config.NextProtos) > 0,
} }
possibleCipherSuites := c.config.cipherSuites()
hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites))
NextCipherSuite:
for _, suiteId := range possibleCipherSuites {
for _, suite := range cipherSuites {
if suite.id != suiteId {
continue
}
// Don't advertise TLS 1.2-only cipher suites unless
// we're attempting TLS 1.2.
if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
continue
}
hello.cipherSuites = append(hello.cipherSuites, suiteId)
continue NextCipherSuite
}
}
t := uint32(c.config.time().Unix()) t := uint32(c.config.time().Unix())
hello.random[0] = byte(t >> 24) hello.random[0] = byte(t >> 24)
hello.random[1] = byte(t >> 16) hello.random[1] = byte(t >> 16)
@ -45,7 +62,10 @@ func (c *Conn) clientHandshake() error {
return errors.New("short read from Rand") return errors.New("short read from Rand")
} }
finishedHash.Write(hello.marshal()) if hello.vers >= VersionTLS12 {
hello.signatureAndHashes = supportedSKXSignatureAlgorithms
}
c.writeRecord(recordTypeHandshake, hello.marshal()) c.writeRecord(recordTypeHandshake, hello.marshal())
msg, err := c.readHandshake() msg, err := c.readHandshake()
@ -56,16 +76,19 @@ func (c *Conn) clientHandshake() error {
if !ok { if !ok {
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
finishedHash.Write(serverHello.marshal())
vers, ok := mutualVersion(serverHello.vers) vers, ok := c.config.mutualVersion(serverHello.vers)
if !ok || vers < versionTLS10 { if !ok || vers < VersionTLS10 {
// TLS 1.0 is the minimum version supported as a client. // TLS 1.0 is the minimum version supported as a client.
return c.sendAlert(alertProtocolVersion) return c.sendAlert(alertProtocolVersion)
} }
c.vers = vers c.vers = vers
c.haveVers = true c.haveVers = true
finishedHash := newFinishedHash(c.vers)
finishedHash.Write(hello.marshal())
finishedHash.Write(serverHello.marshal())
if serverHello.compressionMethod != compressionNone { if serverHello.compressionMethod != compressionNone {
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
@ -121,7 +144,10 @@ func (c *Conn) clientHandshake() error {
} }
} }
if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok { switch certs[0].PublicKey.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey:
break
default:
return c.sendAlert(alertUnsupportedCertificate) return c.sendAlert(alertUnsupportedCertificate)
} }
@ -148,7 +174,7 @@ func (c *Conn) clientHandshake() error {
return err return err
} }
keyAgreement := suite.ka() keyAgreement := suite.ka(c.vers)
skx, ok := msg.(*serverKeyExchangeMsg) skx, ok := msg.(*serverKeyExchangeMsg)
if ok { if ok {
@ -165,7 +191,7 @@ func (c *Conn) clientHandshake() error {
} }
} }
var certToSend *Certificate var chainToSend *Certificate
var certRequested bool var certRequested bool
certReq, ok := msg.(*certificateRequestMsg) certReq, ok := msg.(*certificateRequestMsg)
if ok { if ok {
@ -184,12 +210,13 @@ func (c *Conn) clientHandshake() error {
finishedHash.Write(certReq.marshal()) finishedHash.Write(certReq.marshal())
// For now, we only know how to sign challenges with RSA var rsaAvail, ecdsaAvail bool
rsaAvail := false
for _, certType := range certReq.certificateTypes { for _, certType := range certReq.certificateTypes {
if certType == certTypeRSASign { switch certType {
case certTypeRSASign:
rsaAvail = true rsaAvail = true
break case certTypeECDSASign:
ecdsaAvail = true
} }
} }
@ -197,35 +224,42 @@ func (c *Conn) clientHandshake() error {
// where SignatureAlgorithm is RSA and the Issuer is in // where SignatureAlgorithm is RSA and the Issuer is in
// certReq.certificateAuthorities // certReq.certificateAuthorities
findCert: findCert:
for i, cert := range c.config.Certificates { for i, chain := range c.config.Certificates {
if !rsaAvail { if !rsaAvail && !ecdsaAvail {
continue continue
} }
leaf := cert.Leaf for j, cert := range chain.Certificate {
if leaf == nil { x509Cert := chain.Leaf
if leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil { // parse the certificate if this isn't the leaf
c.sendAlert(alertInternalError) // node, or if chain.Leaf was nil
return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) if j != 0 || x509Cert == nil {
if x509Cert, err = x509.ParseCertificate(cert); err != nil {
c.sendAlert(alertInternalError)
return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error())
}
} }
}
if leaf.PublicKeyAlgorithm != x509.RSA { switch {
continue case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA:
} case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA:
default:
continue findCert
}
if len(certReq.certificateAuthorities) == 0 { if len(certReq.certificateAuthorities) == 0 {
// they gave us an empty list, so just take the // they gave us an empty list, so just take the
// first RSA cert from c.config.Certificates // first RSA cert from c.config.Certificates
certToSend = &cert chainToSend = &chain
break
}
for _, ca := range certReq.certificateAuthorities {
if bytes.Equal(leaf.RawIssuer, ca) {
certToSend = &cert
break findCert break findCert
} }
for _, ca := range certReq.certificateAuthorities {
if bytes.Equal(x509Cert.RawIssuer, ca) {
chainToSend = &chain
break findCert
}
}
} }
} }
@ -246,8 +280,8 @@ func (c *Conn) clientHandshake() error {
// certificate to send. // certificate to send.
if certRequested { if certRequested {
certMsg = new(certificateMsg) certMsg = new(certificateMsg)
if certToSend != nil { if chainToSend != nil {
certMsg.certificates = certToSend.Certificate certMsg.certificates = chainToSend.Certificate
} }
finishedHash.Write(certMsg.marshal()) finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal())
@ -263,12 +297,29 @@ func (c *Conn) clientHandshake() error {
c.writeRecord(recordTypeHandshake, ckx.marshal()) c.writeRecord(recordTypeHandshake, ckx.marshal())
} }
if certToSend != nil { if chainToSend != nil {
certVerify := new(certificateVerifyMsg) var signed []byte
digest := make([]byte, 0, 36) certVerify := &certificateVerifyMsg{
digest = finishedHash.serverMD5.Sum(digest) hasSignatureAndHash: c.vers >= VersionTLS12,
digest = finishedHash.serverSHA1.Sum(digest) }
signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest)
switch key := c.config.Certificates[0].PrivateKey.(type) {
case *ecdsa.PrivateKey:
digest, _, hashId := finishedHash.hashForClientCertificate(signatureECDSA)
r, s, err := ecdsa.Sign(c.config.rand(), key, digest)
if err == nil {
signed, err = asn1.Marshal(ecdsaSignature{r, s})
}
certVerify.signatureAndHash.signature = signatureECDSA
certVerify.signatureAndHash.hash = hashId
case *rsa.PrivateKey:
digest, hashFunc, hashId := finishedHash.hashForClientCertificate(signatureRSA)
signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest)
certVerify.signatureAndHash.signature = signatureRSA
certVerify.signatureAndHash.hash = hashId
default:
err = errors.New("unknown private key type")
}
if err != nil { if err != nil {
return c.sendAlert(alertInternalError) return c.sendAlert(alertInternalError)
} }
@ -282,8 +333,14 @@ func (c *Conn) clientHandshake() error {
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromMasterSecret(c.vers, masterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) keysFromMasterSecret(c.vers, masterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */) var clientCipher interface{}
clientHash := suite.mac(c.vers, clientMAC) var clientHash macFunction
if suite.cipher != nil {
clientCipher = suite.cipher(clientKey, clientIV, false /* not for reading */)
clientHash = suite.mac(c.vers, clientMAC)
} else {
clientCipher = suite.aead(clientKey, clientIV)
}
c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
@ -303,8 +360,14 @@ func (c *Conn) clientHandshake() error {
finishedHash.Write(finished.marshal()) finishedHash.Write(finished.marshal())
c.writeRecord(recordTypeHandshake, finished.marshal()) c.writeRecord(recordTypeHandshake, finished.marshal())
serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */) var serverCipher interface{}
serverHash := suite.mac(c.vers, serverMAC) var serverHash macFunction
if suite.cipher != nil {
serverCipher = suite.cipher(serverKey, serverIV, true /* for reading */)
serverHash = suite.mac(c.vers, serverMAC)
} else {
serverCipher = suite.aead(serverKey, serverIV)
}
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.readRecord(recordTypeChangeCipherSpec) c.readRecord(recordTypeChangeCipherSpec)
if err := c.error(); err != nil { if err := c.error(); err != nil {

File diff suppressed because it is too large Load diff

View file

@ -20,6 +20,7 @@ type clientHelloMsg struct {
supportedPoints []uint8 supportedPoints []uint8
ticketSupported bool ticketSupported bool
sessionTicket []uint8 sessionTicket []uint8
signatureAndHashes []signatureAndHash
} }
func (m *clientHelloMsg) equal(i interface{}) bool { func (m *clientHelloMsg) equal(i interface{}) bool {
@ -40,7 +41,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
eqUint16s(m.supportedCurves, m1.supportedCurves) && eqUint16s(m.supportedCurves, m1.supportedCurves) &&
bytes.Equal(m.supportedPoints, m1.supportedPoints) && bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
m.ticketSupported == m1.ticketSupported && m.ticketSupported == m1.ticketSupported &&
bytes.Equal(m.sessionTicket, m1.sessionTicket) bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes)
} }
func (m *clientHelloMsg) marshal() []byte { func (m *clientHelloMsg) marshal() []byte {
@ -74,6 +76,10 @@ func (m *clientHelloMsg) marshal() []byte {
extensionsLength += len(m.sessionTicket) extensionsLength += len(m.sessionTicket)
numExtensions++ numExtensions++
} }
if len(m.signatureAndHashes) > 0 {
extensionsLength += 2 + 2*len(m.signatureAndHashes)
numExtensions++
}
if numExtensions > 0 { if numExtensions > 0 {
extensionsLength += 4 * numExtensions extensionsLength += 4 * numExtensions
length += 2 + extensionsLength length += 2 + extensionsLength
@ -199,6 +205,25 @@ func (m *clientHelloMsg) marshal() []byte {
copy(z, m.sessionTicket) copy(z, m.sessionTicket)
z = z[len(m.sessionTicket):] z = z[len(m.sessionTicket):]
} }
if len(m.signatureAndHashes) > 0 {
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
z[0] = byte(extensionSignatureAlgorithms >> 8)
z[1] = byte(extensionSignatureAlgorithms)
l := 2 + 2*len(m.signatureAndHashes)
z[2] = byte(l >> 8)
z[3] = byte(l)
z = z[4:]
l -= 2
z[0] = byte(l >> 8)
z[1] = byte(l)
z = z[2:]
for _, sigAndHash := range m.signatureAndHashes {
z[0] = sigAndHash.hash
z[1] = sigAndHash.signature
z = z[2:]
}
}
m.raw = x m.raw = x
@ -249,6 +274,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
m.ocspStapling = false m.ocspStapling = false
m.ticketSupported = false m.ticketSupported = false
m.sessionTicket = nil m.sessionTicket = nil
m.signatureAndHashes = nil
if len(data) == 0 { if len(data) == 0 {
// ClientHello is optionally followed by extension data // ClientHello is optionally followed by extension data
@ -336,6 +362,23 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
// http://tools.ietf.org/html/rfc5077#section-3.2 // http://tools.ietf.org/html/rfc5077#section-3.2
m.ticketSupported = true m.ticketSupported = true
m.sessionTicket = data[:length] m.sessionTicket = data[:length]
case extensionSignatureAlgorithms:
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
if length < 2 || length&1 != 0 {
return false
}
l := int(data[0])<<8 | int(data[1])
if l != length-2 {
return false
}
n := l / 2
d := data[2:]
m.signatureAndHashes = make([]signatureAndHash, n)
for i := range m.signatureAndHashes {
m.signatureAndHashes[i].hash = d[0]
m.signatureAndHashes[i].signature = d[1]
d = d[2:]
}
} }
data = data[length:] data = data[length:]
} }
@ -899,8 +942,14 @@ func (m *nextProtoMsg) unmarshal(data []byte) bool {
} }
type certificateRequestMsg struct { type certificateRequestMsg struct {
raw []byte raw []byte
// hasSignatureAndHash indicates whether this message includes a list
// of signature and hash functions. This change was introduced with TLS
// 1.2.
hasSignatureAndHash bool
certificateTypes []byte certificateTypes []byte
signatureAndHashes []signatureAndHash
certificateAuthorities [][]byte certificateAuthorities [][]byte
} }
@ -912,7 +961,8 @@ func (m *certificateRequestMsg) equal(i interface{}) bool {
return bytes.Equal(m.raw, m1.raw) && return bytes.Equal(m.raw, m1.raw) &&
bytes.Equal(m.certificateTypes, m1.certificateTypes) && bytes.Equal(m.certificateTypes, m1.certificateTypes) &&
eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) &&
eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes)
} }
func (m *certificateRequestMsg) marshal() (x []byte) { func (m *certificateRequestMsg) marshal() (x []byte) {
@ -928,6 +978,10 @@ func (m *certificateRequestMsg) marshal() (x []byte) {
} }
length += casLength length += casLength
if m.hasSignatureAndHash {
length += 2 + 2*len(m.signatureAndHashes)
}
x = make([]byte, 4+length) x = make([]byte, 4+length)
x[0] = typeCertificateRequest x[0] = typeCertificateRequest
x[1] = uint8(length >> 16) x[1] = uint8(length >> 16)
@ -938,6 +992,19 @@ func (m *certificateRequestMsg) marshal() (x []byte) {
copy(x[5:], m.certificateTypes) copy(x[5:], m.certificateTypes)
y := x[5+len(m.certificateTypes):] y := x[5+len(m.certificateTypes):]
if m.hasSignatureAndHash {
n := len(m.signatureAndHashes) * 2
y[0] = uint8(n >> 8)
y[1] = uint8(n)
y = y[2:]
for _, sigAndHash := range m.signatureAndHashes {
y[0] = sigAndHash.hash
y[1] = sigAndHash.signature
y = y[2:]
}
}
y[0] = uint8(casLength >> 8) y[0] = uint8(casLength >> 8)
y[1] = uint8(casLength) y[1] = uint8(casLength)
y = y[2:] y = y[2:]
@ -978,6 +1045,27 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool {
data = data[numCertTypes:] data = data[numCertTypes:]
if m.hasSignatureAndHash {
if len(data) < 2 {
return false
}
sigAndHashLen := uint16(data[0])<<8 | uint16(data[1])
data = data[2:]
if sigAndHashLen&1 != 0 {
return false
}
if len(data) < int(sigAndHashLen) {
return false
}
numSigAndHash := sigAndHashLen / 2
m.signatureAndHashes = make([]signatureAndHash, numSigAndHash)
for i := range m.signatureAndHashes {
m.signatureAndHashes[i].hash = data[0]
m.signatureAndHashes[i].signature = data[1]
data = data[2:]
}
}
if len(data) < 2 { if len(data) < 2 {
return false return false
} }
@ -1013,8 +1101,10 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool {
} }
type certificateVerifyMsg struct { type certificateVerifyMsg struct {
raw []byte raw []byte
signature []byte hasSignatureAndHash bool
signatureAndHash signatureAndHash
signature []byte
} }
func (m *certificateVerifyMsg) equal(i interface{}) bool { func (m *certificateVerifyMsg) equal(i interface{}) bool {
@ -1024,6 +1114,9 @@ func (m *certificateVerifyMsg) equal(i interface{}) bool {
} }
return bytes.Equal(m.raw, m1.raw) && return bytes.Equal(m.raw, m1.raw) &&
m.hasSignatureAndHash == m1.hasSignatureAndHash &&
m.signatureAndHash.hash == m1.signatureAndHash.hash &&
m.signatureAndHash.signature == m1.signatureAndHash.signature &&
bytes.Equal(m.signature, m1.signature) bytes.Equal(m.signature, m1.signature)
} }
@ -1035,14 +1128,23 @@ func (m *certificateVerifyMsg) marshal() (x []byte) {
// See http://tools.ietf.org/html/rfc4346#section-7.4.8 // See http://tools.ietf.org/html/rfc4346#section-7.4.8
siglength := len(m.signature) siglength := len(m.signature)
length := 2 + siglength length := 2 + siglength
if m.hasSignatureAndHash {
length += 2
}
x = make([]byte, 4+length) x = make([]byte, 4+length)
x[0] = typeCertificateVerify x[0] = typeCertificateVerify
x[1] = uint8(length >> 16) x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8) x[2] = uint8(length >> 8)
x[3] = uint8(length) x[3] = uint8(length)
x[4] = uint8(siglength >> 8) y := x[4:]
x[5] = uint8(siglength) if m.hasSignatureAndHash {
copy(x[6:], m.signature) y[0] = m.signatureAndHash.hash
y[1] = m.signatureAndHash.signature
y = y[2:]
}
y[0] = uint8(siglength >> 8)
y[1] = uint8(siglength)
copy(y[2:], m.signature)
m.raw = x m.raw = x
@ -1061,12 +1163,23 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
return false return false
} }
siglength := int(data[4])<<8 + int(data[5]) data = data[4:]
if len(data)-6 != siglength { if m.hasSignatureAndHash {
m.signatureAndHash.hash = data[0]
m.signatureAndHash.signature = data[1]
data = data[2:]
}
if len(data) < 2 {
return false
}
siglength := int(data[0])<<8 + int(data[1])
data = data[2:]
if len(data) != siglength {
return false return false
} }
m.signature = data[6:] m.signature = data
return true return true
} }
@ -1165,3 +1278,16 @@ func eqByteSlices(x, y [][]byte) bool {
} }
return true return true
} }
func eqSignatureAndHashes(x, y []signatureAndHash) bool {
if len(x) != len(y) {
return false
}
for i, v := range x {
v2 := y[i]
if v.hash != v2.hash || v.signature != v2.signature {
return false
}
}
return true
}

View file

@ -135,6 +135,9 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m.sessionTicket = randomBytes(rand.Intn(300), rand) m.sessionTicket = randomBytes(rand.Intn(300), rand)
} }
} }
if rand.Intn(10) > 5 {
m.signatureAndHashes = supportedSKXSignatureAlgorithms
}
return reflect.ValueOf(m) return reflect.ValueOf(m)
} }

View file

@ -6,9 +6,11 @@ package tls
import ( import (
"crypto" "crypto"
"crypto/ecdsa"
"crypto/rsa" "crypto/rsa"
"crypto/subtle" "crypto/subtle"
"crypto/x509" "crypto/x509"
"encoding/asn1"
"errors" "errors"
"io" "io"
) )
@ -21,10 +23,12 @@ type serverHandshakeState struct {
hello *serverHelloMsg hello *serverHelloMsg
suite *cipherSuite suite *cipherSuite
ellipticOk bool ellipticOk bool
ecdsaOk bool
sessionState *sessionState sessionState *sessionState
finishedHash finishedHash finishedHash finishedHash
masterSecret []byte masterSecret []byte
certsFromClient [][]byte certsFromClient [][]byte
cert *Certificate
} }
// serverHandshake performs a TLS handshake as a server. // serverHandshake performs a TLS handshake as a server.
@ -98,7 +102,7 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
if !ok { if !ok {
return false, c.sendAlert(alertUnexpectedMessage) return false, c.sendAlert(alertUnexpectedMessage)
} }
c.vers, ok = mutualVersion(hs.clientHello.vers) c.vers, ok = config.mutualVersion(hs.clientHello.vers)
if !ok { if !ok {
return false, c.sendAlert(alertProtocolVersion) return false, c.sendAlert(alertProtocolVersion)
} }
@ -156,11 +160,25 @@ Curves:
if len(hs.clientHello.serverName) > 0 { if len(hs.clientHello.serverName) > 0 {
c.serverName = hs.clientHello.serverName c.serverName = hs.clientHello.serverName
} }
if hs.clientHello.nextProtoNeg { // Although sending an empty NPN extension is reasonable, Firefox has
// had a bug around this. Best to send nothing at all if
// config.NextProtos is empty. See
// https://code.google.com/p/go/issues/detail?id=5445.
if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 {
hs.hello.nextProtoNeg = true hs.hello.nextProtoNeg = true
hs.hello.nextProtos = config.NextProtos hs.hello.nextProtos = config.NextProtos
} }
if len(config.Certificates) == 0 {
return false, c.sendAlert(alertInternalError)
}
hs.cert = &config.Certificates[0]
if len(hs.clientHello.serverName) > 0 {
hs.cert = config.getCertificateForName(hs.clientHello.serverName)
}
_, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)
if hs.checkForResumption() { if hs.checkForResumption() {
return true, nil return true, nil
} }
@ -175,7 +193,7 @@ Curves:
} }
for _, id := range preferenceList { for _, id := range preferenceList {
if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk); hs.suite != nil { if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil {
break break
} }
} }
@ -199,7 +217,7 @@ func (hs *serverHandshakeState) checkForResumption() bool {
if hs.sessionState.vers > hs.clientHello.vers { if hs.sessionState.vers > hs.clientHello.vers {
return false return false
} }
if vers, ok := mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers { if vers, ok := c.config.mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers {
return false return false
} }
@ -216,7 +234,7 @@ func (hs *serverHandshakeState) checkForResumption() bool {
} }
// Check that we also support the ciphersuite from the session. // Check that we also support the ciphersuite from the session.
hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.ellipticOk) hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers, hs.ellipticOk, hs.ecdsaOk)
if hs.suite == nil { if hs.suite == nil {
return false return false
} }
@ -258,15 +276,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
config := hs.c.config config := hs.c.config
c := hs.c c := hs.c
if len(config.Certificates) == 0 { if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
return c.sendAlert(alertInternalError)
}
cert := &config.Certificates[0]
if len(hs.clientHello.serverName) > 0 {
cert = config.getCertificateForName(hs.clientHello.serverName)
}
if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
hs.hello.ocspStapling = true hs.hello.ocspStapling = true
} }
@ -276,20 +286,20 @@ func (hs *serverHandshakeState) doFullHandshake() error {
c.writeRecord(recordTypeHandshake, hs.hello.marshal()) c.writeRecord(recordTypeHandshake, hs.hello.marshal())
certMsg := new(certificateMsg) certMsg := new(certificateMsg)
certMsg.certificates = cert.Certificate certMsg.certificates = hs.cert.Certificate
hs.finishedHash.Write(certMsg.marshal()) hs.finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal())
if hs.hello.ocspStapling { if hs.hello.ocspStapling {
certStatus := new(certificateStatusMsg) certStatus := new(certificateStatusMsg)
certStatus.statusType = statusTypeOCSP certStatus.statusType = statusTypeOCSP
certStatus.response = cert.OCSPStaple certStatus.response = hs.cert.OCSPStaple
hs.finishedHash.Write(certStatus.marshal()) hs.finishedHash.Write(certStatus.marshal())
c.writeRecord(recordTypeHandshake, certStatus.marshal()) c.writeRecord(recordTypeHandshake, certStatus.marshal())
} }
keyAgreement := hs.suite.ka() keyAgreement := hs.suite.ka(c.vers)
skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello) skx, err := keyAgreement.generateServerKeyExchange(config, hs.cert, hs.clientHello, hs.hello)
if err != nil { if err != nil {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
@ -302,7 +312,14 @@ func (hs *serverHandshakeState) doFullHandshake() error {
if config.ClientAuth >= RequestClientCert { if config.ClientAuth >= RequestClientCert {
// Request a client certificate // Request a client certificate
certReq := new(certificateRequestMsg) certReq := new(certificateRequestMsg)
certReq.certificateTypes = []byte{certTypeRSASign} certReq.certificateTypes = []byte{
byte(certTypeRSASign),
byte(certTypeECDSASign),
}
if c.vers >= VersionTLS12 {
certReq.hasSignatureAndHash = true
certReq.signatureAndHashes = supportedClientCertSignatureAlgorithms
}
// An empty list of certificateAuthorities signals to // An empty list of certificateAuthorities signals to
// the client that it may send any certificate in response // the client that it may send any certificate in response
@ -320,7 +337,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
hs.finishedHash.Write(helloDone.marshal()) hs.finishedHash.Write(helloDone.marshal())
c.writeRecord(recordTypeHandshake, helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal())
var pub *rsa.PublicKey // public key for client auth, if any var pub crypto.PublicKey // public key for client auth, if any
msg, err := c.readHandshake() msg, err := c.readHandshake()
if err != nil { if err != nil {
@ -365,7 +382,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
// If we received a client cert in response to our certificate request message, // If we received a client cert in response to our certificate request message,
// the client will send us a certificateVerifyMsg immediately after the // the client will send us a certificateVerifyMsg immediately after the
// clientKeyExchangeMsg. This message is a MD5SHA1 digest of all preceding // clientKeyExchangeMsg. This message is a digest of all preceding
// handshake-layer messages that is signed using the private key corresponding // handshake-layer messages that is signed using the private key corresponding
// to the client's certificate. This allows us to verify that the client is in // to the client's certificate. This allows us to verify that the client is in
// possession of the private key of the certificate. // possession of the private key of the certificate.
@ -379,10 +396,25 @@ func (hs *serverHandshakeState) doFullHandshake() error {
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
digest := make([]byte, 0, 36) switch key := pub.(type) {
digest = hs.finishedHash.serverMD5.Sum(digest) case *ecdsa.PublicKey:
digest = hs.finishedHash.serverSHA1.Sum(digest) ecdsaSig := new(ecdsaSignature)
err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) if _, err = asn1.Unmarshal(certVerify.signature, ecdsaSig); err != nil {
break
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
err = errors.New("ECDSA signature contained zero or negative values")
break
}
digest, _, _ := hs.finishedHash.hashForClientCertificate(signatureECDSA)
if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) {
err = errors.New("ECDSA verification failure")
break
}
case *rsa.PublicKey:
digest, hashFunc, _ := hs.finishedHash.hashForClientCertificate(signatureRSA)
err = rsa.VerifyPKCS1v15(key, hashFunc, digest, certVerify.signature)
}
if err != nil { if err != nil {
c.sendAlert(alertBadCertificate) c.sendAlert(alertBadCertificate)
return errors.New("could not validate signature of connection nonces: " + err.Error()) return errors.New("could not validate signature of connection nonces: " + err.Error())
@ -391,7 +423,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
hs.finishedHash.Write(certVerify.marshal()) hs.finishedHash.Write(certVerify.marshal())
} }
preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers) preMasterSecret, err := keyAgreement.processClientKeyExchange(config, hs.cert, ckx, c.vers)
if err != nil { if err != nil {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
@ -407,12 +439,20 @@ func (hs *serverHandshakeState) establishKeys() error {
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromMasterSecret(c.vers, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) keysFromMasterSecret(c.vers, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
clientCipher := hs.suite.cipher(clientKey, clientIV, true /* for reading */) var clientCipher, serverCipher interface{}
clientHash := hs.suite.mac(c.vers, clientMAC) var clientHash, serverHash macFunction
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
serverCipher := hs.suite.cipher(serverKey, serverIV, false /* not for reading */) if hs.suite.aead == nil {
serverHash := hs.suite.mac(c.vers, serverMAC) clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
clientHash = hs.suite.mac(c.vers, clientMAC)
serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
serverHash = hs.suite.mac(c.vers, serverMAC)
} else {
clientCipher = hs.suite.aead(clientKey, clientIV)
serverCipher = hs.suite.aead(serverKey, serverIV)
}
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
return nil return nil
@ -502,7 +542,7 @@ func (hs *serverHandshakeState) sendFinished() error {
// processCertsFromClient takes a chain of client certificates either from a // processCertsFromClient takes a chain of client certificates either from a
// Certificates message or from a sessionState and verifies them. It returns // Certificates message or from a sessionState and verifies them. It returns
// the public key of the leaf certificate. // the public key of the leaf certificate.
func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*rsa.PublicKey, error) { func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) {
c := hs.c c := hs.c
hs.certsFromClient = certificates hs.certsFromClient = certificates
@ -549,8 +589,11 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*
} }
if len(certs) > 0 { if len(certs) > 0 {
pub, ok := certs[0].PublicKey.(*rsa.PublicKey) var pub crypto.PublicKey
if !ok { switch key := certs[0].PublicKey.(type) {
case *ecdsa.PublicKey, *rsa.PublicKey:
pub = key
default:
return nil, c.sendAlert(alertUnsupportedCertificate) return nil, c.sendAlert(alertUnsupportedCertificate)
} }
c.peerCertificates = certs c.peerCertificates = certs
@ -562,7 +605,7 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*
// tryCipherSuite returns a cipherSuite with the given id if that cipher suite // tryCipherSuite returns a cipherSuite with the given id if that cipher suite
// is acceptable to use. // is acceptable to use.
func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipticOk bool) *cipherSuite { func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16, ellipticOk, ecdsaOk bool) *cipherSuite {
for _, supported := range supportedCipherSuites { for _, supported := range supportedCipherSuites {
if id == supported { if id == supported {
var candidate *cipherSuite var candidate *cipherSuite
@ -578,7 +621,13 @@ func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipti
} }
// Don't select a ciphersuite which we can't // Don't select a ciphersuite which we can't
// support for this client. // support for this client.
if candidate.elliptic && !ellipticOk { if (candidate.flags&suiteECDHE != 0) && !ellipticOk {
continue
}
if (candidate.flags&suiteECDSA != 0) != ecdsaOk {
continue
}
if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 {
continue continue
} }
return candidate return candidate

File diff suppressed because it is too large Load diff

View file

@ -6,11 +6,14 @@ package tls
import ( import (
"crypto" "crypto"
"crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/md5" "crypto/md5"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/asn1"
"errors" "errors"
"io" "io"
"math/big" "math/big"
@ -36,7 +39,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certifi
} }
ciphertext := ckx.ciphertext ciphertext := ckx.ciphertext
if version != versionSSL30 { if version != VersionSSL30 {
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
if ciphertextLen != len(ckx.ciphertext)-2 { if ciphertextLen != len(ckx.ciphertext)-2 {
return nil, errors.New("bad ClientKeyExchange") return nil, errors.New("bad ClientKeyExchange")
@ -82,34 +85,94 @@ func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello
return preMasterSecret, ckx, nil return preMasterSecret, ckx, nil
} }
// sha1Hash calculates a SHA1 hash over the given byte slices.
func sha1Hash(slices [][]byte) []byte {
hsha1 := sha1.New()
for _, slice := range slices {
hsha1.Write(slice)
}
return hsha1.Sum(nil)
}
// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the // md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
// concatenation of an MD5 and SHA1 hash. // concatenation of an MD5 and SHA1 hash.
func md5SHA1Hash(slices ...[]byte) []byte { func md5SHA1Hash(slices [][]byte) []byte {
md5sha1 := make([]byte, md5.Size+sha1.Size) md5sha1 := make([]byte, md5.Size+sha1.Size)
hmd5 := md5.New() hmd5 := md5.New()
for _, slice := range slices { for _, slice := range slices {
hmd5.Write(slice) hmd5.Write(slice)
} }
copy(md5sha1, hmd5.Sum(nil)) copy(md5sha1, hmd5.Sum(nil))
copy(md5sha1[md5.Size:], sha1Hash(slices))
hsha1 := sha1.New()
for _, slice := range slices {
hsha1.Write(slice)
}
copy(md5sha1[md5.Size:], hsha1.Sum(nil))
return md5sha1 return md5sha1
} }
// sha256Hash implements TLS 1.2's hash function.
func sha256Hash(slices [][]byte) []byte {
h := sha256.New()
for _, slice := range slices {
h.Write(slice)
}
return h.Sum(nil)
}
// hashForServerKeyExchange hashes the given slices and returns their digest
// and the identifier of the hash function used. The hashFunc argument is only
// used for >= TLS 1.2 and precisely identifies the hash function to use.
func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) {
if version >= VersionTLS12 {
switch hashFunc {
case hashSHA256:
return sha256Hash(slices), crypto.SHA256, nil
case hashSHA1:
return sha1Hash(slices), crypto.SHA1, nil
default:
return nil, crypto.Hash(0), errors.New("tls: unknown hash function used by peer")
}
}
if sigType == signatureECDSA {
return sha1Hash(slices), crypto.SHA1, nil
}
return md5SHA1Hash(slices), crypto.MD5SHA1, nil
}
// pickTLS12HashForSignature returns a TLS 1.2 hash identifier for signing a
// ServerKeyExchange given the signature type being used and the client's
// advertized list of supported signature and hash combinations.
func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatureAndHash) (uint8, error) {
if len(clientSignatureAndHashes) == 0 {
// If the client didn't specify any signature_algorithms
// extension then we can assume that it supports SHA1. See
// http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
return hashSHA1, nil
}
for _, sigAndHash := range clientSignatureAndHashes {
if sigAndHash.signature != sigType {
continue
}
switch sigAndHash.hash {
case hashSHA1, hashSHA256:
return sigAndHash.hash, nil
}
}
return 0, errors.New("tls: client doesn't support any common hash functions")
}
// ecdheRSAKeyAgreement implements a TLS key agreement where the server // ecdheRSAKeyAgreement implements a TLS key agreement where the server
// generates a ephemeral EC public/private key pair and signs it. The // generates a ephemeral EC public/private key pair and signs it. The
// pre-master secret is then calculated using ECDH. // pre-master secret is then calculated using ECDH. The signature may
type ecdheRSAKeyAgreement struct { // either be ECDSA or RSA.
type ecdheKeyAgreement struct {
version uint16
sigType uint8
privateKey []byte privateKey []byte
curve elliptic.Curve curve elliptic.Curve
x, y *big.Int x, y *big.Int
} }
func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
var curveid uint16 var curveid uint16
Curve: Curve:
@ -150,16 +213,55 @@ Curve:
serverECDHParams[3] = byte(len(ecdhePublic)) serverECDHParams[3] = byte(len(ecdhePublic))
copy(serverECDHParams[4:], ecdhePublic) copy(serverECDHParams[4:], ecdhePublic)
md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams) var tls12HashId uint8
sig, err := rsa.SignPKCS1v15(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1) if ka.version >= VersionTLS12 {
if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes); err != nil {
return nil, err
}
}
digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, hello.random, serverECDHParams)
if err != nil { if err != nil {
return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) return nil, err
}
var sig []byte
switch ka.sigType {
case signatureECDSA:
privKey, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
if !ok {
return nil, errors.New("ECDHE ECDSA requires an ECDSA server private key")
}
r, s, err := ecdsa.Sign(config.rand(), privKey, digest)
if err != nil {
return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
}
sig, err = asn1.Marshal(ecdsaSignature{r, s})
case signatureRSA:
privKey, ok := cert.PrivateKey.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("ECDHE RSA requires a RSA server private key")
}
sig, err = rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest)
if err != nil {
return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
}
default:
return nil, errors.New("unknown ECDHE signature algorithm")
} }
skx := new(serverKeyExchangeMsg) skx := new(serverKeyExchangeMsg)
skx.key = make([]byte, len(serverECDHParams)+2+len(sig)) sigAndHashLen := 0
if ka.version >= VersionTLS12 {
sigAndHashLen = 2
}
skx.key = make([]byte, len(serverECDHParams)+sigAndHashLen+2+len(sig))
copy(skx.key, serverECDHParams) copy(skx.key, serverECDHParams)
k := skx.key[len(serverECDHParams):] k := skx.key[len(serverECDHParams):]
if ka.version >= VersionTLS12 {
k[0] = tls12HashId
k[1] = ka.sigType
k = k[2:]
}
k[0] = byte(len(sig) >> 8) k[0] = byte(len(sig) >> 8)
k[1] = byte(len(sig)) k[1] = byte(len(sig))
copy(k[2:], sig) copy(k[2:], sig)
@ -167,7 +269,7 @@ Curve:
return skx, nil return skx, nil
} }
func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, 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, errors.New("bad ClientKeyExchange") return nil, errors.New("bad ClientKeyExchange")
} }
@ -185,7 +287,7 @@ func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, cert *C
var errServerKeyExchange = errors.New("invalid ServerKeyExchange") var errServerKeyExchange = errors.New("invalid ServerKeyExchange")
func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
if len(skx.key) < 4 { if len(skx.key) < 4 {
return errServerKeyExchange return errServerKeyExchange
} }
@ -219,17 +321,62 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH
if len(sig) < 2 { if len(sig) < 2 {
return errServerKeyExchange return errServerKeyExchange
} }
var tls12HashId uint8
if ka.version >= VersionTLS12 {
// handle SignatureAndHashAlgorithm
var sigAndHash []uint8
sigAndHash, sig = sig[:2], sig[2:]
if sigAndHash[1] != ka.sigType {
return errServerKeyExchange
}
tls12HashId = sigAndHash[0]
if len(sig) < 2 {
return errServerKeyExchange
}
}
sigLen := int(sig[0])<<8 | int(sig[1]) sigLen := int(sig[0])<<8 | int(sig[1])
if sigLen+2 != len(sig) { if sigLen+2 != len(sig) {
return errServerKeyExchange return errServerKeyExchange
} }
sig = sig[2:] sig = sig[2:]
md5sha1 := md5SHA1Hash(clientHello.random, serverHello.random, serverECDHParams) digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, serverHello.random, serverECDHParams)
return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), crypto.MD5SHA1, md5sha1, sig) if err != nil {
return err
}
switch ka.sigType {
case signatureECDSA:
pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
if !ok {
return errors.New("ECDHE ECDSA requires a ECDSA server public key")
}
ecdsaSig := new(ecdsaSignature)
if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil {
return err
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
return errors.New("ECDSA signature contained zero or negative values")
}
if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) {
return errors.New("ECDSA verification failure")
}
case signatureRSA:
pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
return errors.New("ECDHE RSA requires a RSA server public key")
}
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil {
return err
}
default:
return errors.New("unknown ECDHE signature algorithm")
}
return nil
} }
func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
if ka.curve == nil { if ka.curve == nil {
return nil, nil, errors.New("missing ServerKeyExchange message") return nil, nil, errors.New("missing ServerKeyExchange message")
} }

View file

@ -5,9 +5,11 @@
package tls package tls
import ( import (
"crypto"
"crypto/hmac" "crypto/hmac"
"crypto/md5" "crypto/md5"
"crypto/sha1" "crypto/sha1"
"crypto/sha256"
"hash" "hash"
) )
@ -43,8 +45,8 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) {
} }
} }
// pRF10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5. // prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5.
func pRF10(result, secret, label, seed []byte) { func prf10(result, secret, label, seed []byte) {
hashSHA1 := sha1.New hashSHA1 := sha1.New
hashMD5 := md5.New hashMD5 := md5.New
@ -62,9 +64,18 @@ func pRF10(result, secret, label, seed []byte) {
} }
} }
// pRF30 implements the SSL 3.0 pseudo-random function, as defined in // prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, section 5.
func prf12(result, secret, label, seed []byte) {
labelAndSeed := make([]byte, len(label)+len(seed))
copy(labelAndSeed, label)
copy(labelAndSeed[len(label):], seed)
pHash(result, secret, labelAndSeed, sha256.New)
}
// prf30 implements the SSL 3.0 pseudo-random function, as defined in
// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6. // www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6.
func pRF30(result, secret, label, seed []byte) { func prf30(result, secret, label, seed []byte) {
hashSHA1 := sha1.New() hashSHA1 := sha1.New()
hashMD5 := md5.New() hashMD5 := md5.New()
@ -106,19 +117,27 @@ var keyExpansionLabel = []byte("key expansion")
var clientFinishedLabel = []byte("client finished") var clientFinishedLabel = []byte("client finished")
var serverFinishedLabel = []byte("server finished") var serverFinishedLabel = []byte("server finished")
func prfForVersion(version uint16) func(result, secret, label, seed []byte) {
switch version {
case VersionSSL30:
return prf30
case VersionTLS10, VersionTLS11:
return prf10
case VersionTLS12:
return prf12
default:
panic("unknown version")
}
}
// masterFromPreMasterSecret generates the master secret from the pre-master // masterFromPreMasterSecret generates the master secret from the pre-master
// secret. See http://tools.ietf.org/html/rfc5246#section-8.1 // secret. See http://tools.ietf.org/html/rfc5246#section-8.1
func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []byte { func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []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)
prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) prfForVersion(version)(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
return masterSecret return masterSecret
} }
@ -126,18 +145,13 @@ func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, se
// 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 keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { func keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (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)], 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)
prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) prfForVersion(version)(keyMaterial, masterSecret, keyExpansionLabel, seed[0:])
clientMAC = keyMaterial[:macLen] clientMAC = keyMaterial[:macLen]
keyMaterial = keyMaterial[macLen:] keyMaterial = keyMaterial[macLen:]
serverMAC = keyMaterial[:macLen] serverMAC = keyMaterial[:macLen]
@ -153,37 +167,34 @@ func keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRand
} }
func newFinishedHash(version uint16) finishedHash { func newFinishedHash(version uint16) finishedHash {
return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version} if version >= VersionTLS12 {
return finishedHash{sha256.New(), sha256.New(), nil, nil, version}
}
return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.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 {
clientMD5 hash.Hash client hash.Hash
clientSHA1 hash.Hash server hash.Hash
serverMD5 hash.Hash
serverSHA1 hash.Hash // Prior to TLS 1.2, an additional MD5 hash is required.
version uint16 clientMD5 hash.Hash
serverMD5 hash.Hash
version uint16
} }
func (h finishedHash) Write(msg []byte) (n int, err error) { func (h finishedHash) Write(msg []byte) (n int, err error) {
h.clientMD5.Write(msg) h.client.Write(msg)
h.clientSHA1.Write(msg) h.server.Write(msg)
h.serverMD5.Write(msg)
h.serverSHA1.Write(msg)
return len(msg), nil
}
// finishedSum10 calculates the contents of the verify_data member of a TLSv1 if h.version < VersionTLS12 {
// Finished message given the MD5 and SHA1 hashes of a set of handshake h.clientMD5.Write(msg)
// messages. h.serverMD5.Write(msg)
func finishedSum10(md5, sha1, label, masterSecret []byte) []byte { }
seed := make([]byte, len(md5)+len(sha1)) return len(msg), nil
copy(seed, md5)
copy(seed[len(md5):], sha1)
out := make([]byte, finishedVerifyLength)
pRF10(out, masterSecret, label, seed)
return out
} }
// finishedSum30 calculates the contents of the verify_data member of a SSLv3 // finishedSum30 calculates the contents of the verify_data member of a SSLv3
@ -224,23 +235,57 @@ 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 { if h.version == VersionSSL30 {
return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic) return finishedSum30(h.clientMD5, h.client, masterSecret, ssl3ClientFinishedMagic)
} }
md5 := h.clientMD5.Sum(nil) out := make([]byte, finishedVerifyLength)
sha1 := h.clientSHA1.Sum(nil) if h.version >= VersionTLS12 {
return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret) seed := h.client.Sum(nil)
prf12(out, masterSecret, clientFinishedLabel, seed)
} else {
seed := make([]byte, 0, md5.Size+sha1.Size)
seed = h.clientMD5.Sum(seed)
seed = h.client.Sum(seed)
prf10(out, masterSecret, clientFinishedLabel, seed)
}
return out
} }
// 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 { if h.version == VersionSSL30 {
return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic) return finishedSum30(h.serverMD5, h.server, masterSecret, ssl3ServerFinishedMagic)
} }
md5 := h.serverMD5.Sum(nil) out := make([]byte, finishedVerifyLength)
sha1 := h.serverSHA1.Sum(nil) if h.version >= VersionTLS12 {
return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret) seed := h.server.Sum(nil)
prf12(out, masterSecret, serverFinishedLabel, seed)
} else {
seed := make([]byte, 0, md5.Size+sha1.Size)
seed = h.serverMD5.Sum(seed)
seed = h.server.Sum(seed)
prf10(out, masterSecret, serverFinishedLabel, seed)
}
return out
}
// hashForClientCertificate returns a digest, hash function, and TLS 1.2 hash
// id suitable for signing by a TLS client certificate.
func (h finishedHash) hashForClientCertificate(sigType uint8) ([]byte, crypto.Hash, uint8) {
if h.version >= VersionTLS12 {
digest := h.server.Sum(nil)
return digest, crypto.SHA256, hashSHA256
}
if sigType == signatureECDSA {
digest := h.server.Sum(nil)
return digest, crypto.SHA1, hashSHA1
}
digest := make([]byte, 0, 36)
digest = h.serverMD5.Sum(digest)
digest = h.server.Sum(digest)
return digest, crypto.MD5SHA1, 0 /* not specified in TLS 1.2. */
} }

Some files were not shown because too many files have changed in this diff Show more