libgo: Update to current sources.

From-SVN: r192704
This commit is contained in:
Ian Lance Taylor 2012-10-23 04:31:11 +00:00
parent 0b7463235f
commit 4ccad563d2
554 changed files with 56940 additions and 12634 deletions

View File

@ -1251,6 +1251,7 @@ Gogo::clear_file_scope()
this->package_->bindings()->clear_file_scope(); this->package_->bindings()->clear_file_scope();
// Warn about packages which were imported but not used. // Warn about packages which were imported but not used.
bool quiet = saw_errors();
for (Packages::iterator p = this->packages_.begin(); for (Packages::iterator p = this->packages_.begin();
p != this->packages_.end(); p != this->packages_.end();
++p) ++p)
@ -1260,7 +1261,7 @@ Gogo::clear_file_scope()
&& package->is_imported() && package->is_imported()
&& !package->used() && !package->used()
&& !package->uses_sink_alias() && !package->uses_sink_alias()
&& !saw_errors()) && !quiet)
error_at(package->location(), "imported and not used: %s", error_at(package->location(), "imported and not used: %s",
Gogo::message_name(package->package_name()).c_str()); Gogo::message_name(package->package_name()).c_str());
package->clear_is_imported(); package->clear_is_imported();

View File

@ -32,6 +32,8 @@ enum Runtime_function_type
RFT_BOOLPTR, RFT_BOOLPTR,
// Go type int, C type int. // Go type int, C type int.
RFT_INT, RFT_INT,
// Go type int32, C type int32_t.
RFT_INT32,
// Go type int64, C type int64_t. // Go type int64, C type int64_t.
RFT_INT64, RFT_INT64,
// Go type uint64, C type uint64_t. // Go type uint64, C type uint64_t.
@ -102,6 +104,10 @@ runtime_function_type(Runtime_function_type bft)
t = Type::lookup_integer_type("int"); t = Type::lookup_integer_type("int");
break; break;
case RFT_INT32:
t = Type::lookup_integer_type("int32");
break;
case RFT_INT64: case RFT_INT64:
t = Type::lookup_integer_type("int64"); t = Type::lookup_integer_type("int64");
break; break;
@ -206,6 +212,7 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e,
case RFT_BOOL: case RFT_BOOL:
case RFT_BOOLPTR: case RFT_BOOLPTR:
case RFT_INT: case RFT_INT:
case RFT_INT32:
case RFT_INT64: case RFT_INT64:
case RFT_UINT64: case RFT_UINT64:
case RFT_UINTPTR: case RFT_UINTPTR:

View File

@ -148,27 +148,28 @@ DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P3(TYPE, CHAN, POINTER),
// Start building a select statement. // Start building a select statement.
DEF_GO_RUNTIME(NEWSELECT, "runtime.newselect", P1(INT), R1(POINTER)) DEF_GO_RUNTIME(NEWSELECT, "runtime.newselect", P1(INT32), R1(POINTER))
// Add a default clause to a select statement. // Add a default clause to a select statement.
DEF_GO_RUNTIME(SELECTDEFAULT, "runtime.selectdefault", P2(POINTER, INT), R0()) DEF_GO_RUNTIME(SELECTDEFAULT, "runtime.selectdefault",
P2(POINTER, INT32), R0())
// Add a send clause to a select statement. // Add a send clause to a select statement.
DEF_GO_RUNTIME(SELECTSEND, "runtime.selectsend", DEF_GO_RUNTIME(SELECTSEND, "runtime.selectsend",
P4(POINTER, CHAN, POINTER, INT), R0()) P4(POINTER, CHAN, POINTER, INT32), R0())
// Add a receive clause to a select statement, for a clause which does // Add a receive clause to a select statement, for a clause which does
// not check whether the channel is closed. // not check whether the channel is closed.
DEF_GO_RUNTIME(SELECTRECV, "runtime.selectrecv", DEF_GO_RUNTIME(SELECTRECV, "runtime.selectrecv",
P4(POINTER, CHAN, POINTER, INT), R0()) P4(POINTER, CHAN, POINTER, INT32), R0())
// Add a receive clause to a select statement, for a clause which does // Add a receive clause to a select statement, for a clause which does
// check whether the channel is closed. // check whether the channel is closed.
DEF_GO_RUNTIME(SELECTRECV2, "runtime.selectrecv2", DEF_GO_RUNTIME(SELECTRECV2, "runtime.selectrecv2",
P5(POINTER, CHAN, POINTER, BOOLPTR, INT), R0()) P5(POINTER, CHAN, POINTER, BOOLPTR, INT32), R0())
// Run a select, returning the index of the selected clause. // Run a select, returning the index of the selected clause.
DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT)) DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT32))
// Panic. // Panic.

View File

@ -4841,6 +4841,8 @@ Select_clauses::get_backend(Translate_context* context,
std::vector<std::vector<Bexpression*> > cases(count); std::vector<std::vector<Bexpression*> > cases(count);
std::vector<Bstatement*> clauses(count); std::vector<Bstatement*> clauses(count);
Type* int32_type = Type::lookup_integer_type("int32");
int i = 0; int i = 0;
for (Clauses::iterator p = this->clauses_.begin(); for (Clauses::iterator p = this->clauses_.begin();
p != this->clauses_.end(); p != this->clauses_.end();
@ -4849,7 +4851,8 @@ Select_clauses::get_backend(Translate_context* context,
int index = p->index(); int index = p->index();
mpz_t ival; mpz_t ival;
mpz_init_set_ui(ival, index); mpz_init_set_ui(ival, index);
Expression* index_expr = Expression::make_integer(&ival, NULL, location); Expression* index_expr = Expression::make_integer(&ival, int32_type,
location);
mpz_clear(ival); mpz_clear(ival);
cases[i].push_back(tree_to_expr(index_expr->get_tree(context))); cases[i].push_back(tree_to_expr(index_expr->get_tree(context)));

View File

@ -12,7 +12,7 @@ package main
import ( import (
"io/ioutil" // GCCGO_ERROR "imported and not used" "io/ioutil" // GCCGO_ERROR "imported and not used"
"net/http" "net/http"
"os" "os" // GCCGO_ERROR "imported and not used"
) )
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {

View File

@ -38,7 +38,7 @@ func BenchmarkSlowNonASCII(b *testing.B) {
} }
func main() { func main() {
os.Args = []string{os.Args[0], "-test.benchtime=0.1"} os.Args = []string{os.Args[0], "-test.benchtime=100ms"}
flag.Parse() flag.Parse()
rslow := testing.Benchmark(BenchmarkSlowNonASCII) rslow := testing.Benchmark(BenchmarkSlowNonASCII)

View File

@ -1,4 +1,4 @@
2d8bc3c94ecb 291d9f1baf75
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

@ -230,6 +230,21 @@ toolexeclibgoexp_DATA = \
exp/types.gox \ exp/types.gox \
exp/utf8string.gox exp/utf8string.gox
toolexeclibgoexphtmldir = $(toolexeclibgoexpdir)/html
toolexeclibgoexphtml_DATA = \
exp/html/atom.gox
toolexeclibgoexplocaledir = $(toolexeclibgoexpdir)/locale
toolexeclibgoexplocale_DATA = \
exp/locale/collate.gox
toolexeclibgoexplocalecollatedir = $(toolexeclibgoexplocaledir)/collate
toolexeclibgoexplocalecollate_DATA = \
exp/locale/collate/build.gox
toolexeclibgogodir = $(toolexeclibgodir)/go toolexeclibgogodir = $(toolexeclibgodir)/go
toolexeclibgogo_DATA = \ toolexeclibgogo_DATA = \
@ -483,6 +498,7 @@ runtime_files = \
runtime/go-unwind.c \ runtime/go-unwind.c \
runtime/chan.c \ runtime/chan.c \
runtime/cpuprof.c \ runtime/cpuprof.c \
runtime/lfstack.c \
$(runtime_lock_files) \ $(runtime_lock_files) \
runtime/mcache.c \ runtime/mcache.c \
runtime/mcentral.c \ runtime/mcentral.c \
@ -492,6 +508,8 @@ runtime_files = \
runtime/mgc0.c \ runtime/mgc0.c \
runtime/mheap.c \ runtime/mheap.c \
runtime/msize.c \ runtime/msize.c \
runtime/panic.c \
runtime/parfor.c \
runtime/print.c \ runtime/print.c \
runtime/proc.c \ runtime/proc.c \
runtime/runtime.c \ runtime/runtime.c \
@ -656,16 +674,16 @@ go_net_newpollserver_file = go/net/newpollserver_rtems.go
else # !LIBGO_IS_RTEMS else # !LIBGO_IS_RTEMS
if LIBGO_IS_LINUX if LIBGO_IS_LINUX
go_net_fd_os_file = go/net/fd_linux.go go_net_fd_os_file = go/net/fd_linux.go
go_net_newpollserver_file = go/net/newpollserver.go go_net_newpollserver_file = go/net/newpollserver_unix.go
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_netbsd.go go_net_fd_os_file = go/net/fd_netbsd.go
go_net_newpollserver_file = go/net/newpollserver.go go_net_newpollserver_file = go/net/newpollserver_unix.go
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
# something better. # something better.
go_net_fd_os_file = go/net/fd_select.go go_net_fd_os_file = go/net/fd_select.go
go_net_newpollserver_file = go/net/newpollserver.go go_net_newpollserver_file = go/net/newpollserver_unix.go
endif # !LIBGO_IS_NETBSD endif # !LIBGO_IS_NETBSD
endif # !LIBGO_IS_LINUX endif # !LIBGO_IS_LINUX
endif # !LIBGO_IS_RTEMS endif # !LIBGO_IS_RTEMS
@ -674,13 +692,13 @@ if LIBGO_IS_LINUX
go_net_cgo_file = go/net/cgo_linux.go go_net_cgo_file = go/net/cgo_linux.go
go_net_sock_file = go/net/sock_linux.go go_net_sock_file = go/net/sock_linux.go
go_net_sockopt_file = go/net/sockopt_linux.go go_net_sockopt_file = go/net/sockopt_linux.go
go_net_sockoptip_file = go/net/sockoptip_linux.go go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
else else
if LIBGO_IS_IRIX if LIBGO_IS_IRIX
go_net_cgo_file = go/net/cgo_linux.go go_net_cgo_file = go/net/cgo_linux.go
go_net_sock_file = go/net/sock_linux.go go_net_sock_file = go/net/sock_linux.go
go_net_sockopt_file = go/net/sockopt_linux.go go_net_sockopt_file = go/net/sockopt_linux.go
go_net_sockoptip_file = go/net/sockoptip_linux.go go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
else else
if LIBGO_IS_SOLARIS if LIBGO_IS_SOLARIS
go_net_cgo_file = go/net/cgo_linux.go go_net_cgo_file = go/net/cgo_linux.go
@ -692,12 +710,19 @@ if LIBGO_IS_FREEBSD
go_net_cgo_file = go/net/cgo_bsd.go go_net_cgo_file = go/net/cgo_bsd.go
go_net_sock_file = go/net/sock_bsd.go go_net_sock_file = go/net/sock_bsd.go
go_net_sockopt_file = go/net/sockopt_bsd.go go_net_sockopt_file = go/net/sockopt_bsd.go
go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_freebsd.go go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
else
if LIBGO_IS_NETBSD
go_net_cgo_file = go/net/cgo_netbsd.go
go_net_sock_file = go/net/sock_bsd.go
go_net_sockopt_file = go/net/sockopt_bsd.go
go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
else else
go_net_cgo_file = go/net/cgo_bsd.go go_net_cgo_file = go/net/cgo_bsd.go
go_net_sock_file = go/net/sock_bsd.go go_net_sock_file = go/net/sock_bsd.go
go_net_sockopt_file = go/net/sockopt_bsd.go go_net_sockopt_file = go/net/sockopt_bsd.go
go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_netbsd.go go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
endif
endif endif
endif endif
endif endif
@ -706,8 +731,12 @@ endif
if LIBGO_IS_LINUX if LIBGO_IS_LINUX
go_net_sendfile_file = go/net/sendfile_linux.go go_net_sendfile_file = go/net/sendfile_linux.go
else else
if LIBGO_IS_FREEBSD
go_net_sendfile_file = go/net/sendfile_freebsd.go
else
go_net_sendfile_file = go/net/sendfile_stub.go go_net_sendfile_file = go/net/sendfile_stub.go
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
@ -725,13 +754,12 @@ go_net_files = \
go/net/dial.go \ go/net/dial.go \
go/net/dnsclient.go \ go/net/dnsclient.go \
go/net/dnsclient_unix.go \ go/net/dnsclient_unix.go \
go/net/dnsconfig.go \ go/net/dnsconfig_unix.go \
go/net/dnsmsg.go \ go/net/dnsmsg.go \
go/net/doc.go \
$(go_net_newpollserver_file) \ $(go_net_newpollserver_file) \
go/net/fd.go \ go/net/fd_unix.go \
$(go_net_fd_os_file) \ $(go_net_fd_os_file) \
go/net/file.go \ go/net/file_unix.go \
go/net/hosts.go \ go/net/hosts.go \
go/net/interface.go \ go/net/interface.go \
$(go_net_interface_file) \ $(go_net_interface_file) \
@ -740,6 +768,7 @@ go_net_files = \
go/net/iprawsock_posix.go \ go/net/iprawsock_posix.go \
go/net/ipsock.go \ go/net/ipsock.go \
go/net/ipsock_posix.go \ go/net/ipsock_posix.go \
go/net/lookup.go \
go/net/lookup_unix.go \ go/net/lookup_unix.go \
go/net/mac.go \ go/net/mac.go \
go/net/net.go \ go/net/net.go \
@ -747,12 +776,12 @@ go_net_files = \
go/net/parse.go \ go/net/parse.go \
go/net/pipe.go \ go/net/pipe.go \
go/net/port.go \ go/net/port.go \
go/net/port_unix.go \
$(go_net_sendfile_file) \ $(go_net_sendfile_file) \
go/net/sock.go \ go/net/sock_posix.go \
$(go_net_sock_file) \ $(go_net_sock_file) \
go/net/sockopt.go \ go/net/sockopt_posix.go \
$(go_net_sockopt_file) \ $(go_net_sockopt_file) \
go/net/sockoptip.go \
$(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 \
@ -831,6 +860,7 @@ go_path_files = \
go_reflect_files = \ go_reflect_files = \
go/reflect/deepequal.go \ go/reflect/deepequal.go \
go/reflect/makefunc.go \
go/reflect/type.go \ go/reflect/type.go \
go/reflect/value.go go/reflect/value.go
@ -882,12 +912,14 @@ go_strconv_files = \
go_strings_files = \ 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/strings.go go/strings/strings.go
go_sync_files = \ go_sync_files = \
go/sync/cond.go \ go/sync/cond.go \
go/sync/mutex.go \ go/sync/mutex.go \
go/sync/once.go \ go/sync/once.go \
go/sync/race0.go \
go/sync/runtime.go \ go/sync/runtime.go \
go/sync/rwmutex.go \ go/sync/rwmutex.go \
go/sync/waitgroup.go go/sync/waitgroup.go
@ -930,11 +962,28 @@ go_unicode_files = \
go/unicode/letter.go \ go/unicode/letter.go \
go/unicode/tables.go go/unicode/tables.go
if LIBGO_IS_LINUX
archive_tar_atim_file = go/archive/tar/stat_atim.go
endif
if LIBGO_IS_OPENBSD
archive_tar_atim_file = go/archive/tar/stat_atim.go
endif
if LIBGO_IS_DARWIN
archive_tar_atim_file = go/archive/tar/stat_atimespec.go
endif
if LIBGO_IS_FREEBSD
archive_tar_atim_file = go/archive/tar/stat_atimespec.go
endif
if LIBGO_IS_NETBSD
archive_tar_atim_file = go/archive/tar/stat_atimespec.go
endif
go_archive_tar_files = \ go_archive_tar_files = \
go/archive/tar/common.go \ go/archive/tar/common.go \
go/archive/tar/reader.go \ go/archive/tar/reader.go \
go/archive/tar/writer.go go/archive/tar/stat_unix.go \
go/archive/tar/writer.go \
$(archive_tar_atim_file)
go_archive_zip_files = \ go_archive_zip_files = \
go/archive/zip/reader.go \ go/archive/zip/reader.go \
@ -948,6 +997,7 @@ go_compress_bzip2_files = \
go/compress/bzip2/move_to_front.go go/compress/bzip2/move_to_front.go
go_compress_flate_files = \ go_compress_flate_files = \
go/compress/flate/copy.go \
go/compress/flate/deflate.go \ go/compress/flate/deflate.go \
go/compress/flate/huffman_bit_writer.go \ go/compress/flate/huffman_bit_writer.go \
go/compress/flate/huffman_code.go \ go/compress/flate/huffman_code.go \
@ -979,6 +1029,7 @@ go_container_ring_files = \
go_crypto_aes_files = \ go_crypto_aes_files = \
go/crypto/aes/block.go \ go/crypto/aes/block.go \
go/crypto/aes/cipher.go \ go/crypto/aes/cipher.go \
go/crypto/aes/cipher_generic.go \
go/crypto/aes/const.go go/crypto/aes/const.go
go_crypto_cipher_files = \ go_crypto_cipher_files = \
go/crypto/cipher/cbc.go \ go/crypto/cipher/cbc.go \
@ -1033,9 +1084,11 @@ go_crypto_tls_files = \
go/crypto/tls/handshake_server.go \ go/crypto/tls/handshake_server.go \
go/crypto/tls/key_agreement.go \ go/crypto/tls/key_agreement.go \
go/crypto/tls/prf.go \ go/crypto/tls/prf.go \
go/crypto/tls/ticket.go \
go/crypto/tls/tls.go go/crypto/tls/tls.go
go_crypto_x509_files = \ go_crypto_x509_files = \
go/crypto/x509/cert_pool.go \ go/crypto/x509/cert_pool.go \
go/crypto/x509/pem_decrypt.go \
go/crypto/x509/pkcs1.go \ go/crypto/x509/pkcs1.go \
go/crypto/x509/pkcs8.go \ go/crypto/x509/pkcs8.go \
go/crypto/x509/root.go \ go/crypto/x509/root.go \
@ -1130,8 +1183,26 @@ go_exp_html_files = \
go/exp/html/parse.go \ go/exp/html/parse.go \
go/exp/html/render.go \ go/exp/html/render.go \
go/exp/html/token.go go/exp/html/token.go
go_exp_html_atom_files = \
go/exp/html/atom/atom.go \
go/exp/html/atom/table.go
go_exp_inotify_files = \ go_exp_inotify_files = \
go/exp/inotify/inotify_linux.go go/exp/inotify/inotify_linux.go
go_exp_locale_collate_files = \
go/exp/locale/collate/colelem.go \
go/exp/locale/collate/collate.go \
go/exp/locale/collate/contract.go \
go/exp/locale/collate/export.go \
go/exp/locale/collate/table.go \
go/exp/locale/collate/tables.go \
go/exp/locale/collate/trie.go
go_exp_locale_collate_build_files = \
go/exp/locale/collate/build/builder.go \
go/exp/locale/collate/build/colelem.go \
go/exp/locale/collate/build/contract.go \
go/exp/locale/collate/build/order.go \
go/exp/locale/collate/build/table.go \
go/exp/locale/collate/build/trie.go
go_exp_norm_files = \ go_exp_norm_files = \
go/exp/norm/composition.go \ go/exp/norm/composition.go \
go/exp/norm/forminfo.go \ go/exp/norm/forminfo.go \
@ -1161,6 +1232,7 @@ go_exp_utf8string_files = \
go_go_ast_files = \ go_go_ast_files = \
go/go/ast/ast.go \ go/go/ast/ast.go \
go/go/ast/commentmap.go \
go/go/ast/filter.go \ go/go/ast/filter.go \
go/go/ast/import.go \ go/go/ast/import.go \
go/go/ast/print.go \ go/go/ast/print.go \
@ -1170,6 +1242,7 @@ go_go_ast_files = \
go_go_build_files = \ go_go_build_files = \
go/go/build/build.go \ go/go/build/build.go \
go/go/build/doc.go \ go/go/build/doc.go \
go/go/build/read.go \
syslist.go syslist.go
go_go_doc_files = \ go_go_doc_files = \
go/go/doc/comment.go \ go/go/doc/comment.go \
@ -1235,6 +1308,7 @@ go_image_jpeg_files = \
go/image/jpeg/writer.go go/image/jpeg/writer.go
go_image_png_files = \ go_image_png_files = \
go/image/png/paeth.go \
go/image/png/reader.go \ go/image/png/reader.go \
go/image/png/writer.go go/image/png/writer.go
@ -1243,6 +1317,7 @@ go_index_suffixarray_files = \
go/index/suffixarray/suffixarray.go go/index/suffixarray/suffixarray.go
go_io_ioutil_files = \ go_io_ioutil_files = \
go/io/ioutil/blackhole.go \
go/io/ioutil/ioutil.go \ go/io/ioutil/ioutil.go \
go/io/ioutil/tempfile.go go/io/ioutil/tempfile.go
@ -1358,6 +1433,7 @@ go_path_filepath_files = \
go_regexp_syntax_files = \ go_regexp_syntax_files = \
go/regexp/syntax/compile.go \ go/regexp/syntax/compile.go \
go/regexp/syntax/doc.go \
go/regexp/syntax/parse.go \ go/regexp/syntax/parse.go \
go/regexp/syntax/perl_groups.go \ go/regexp/syntax/perl_groups.go \
go/regexp/syntax/prog.go \ go/regexp/syntax/prog.go \
@ -1544,6 +1620,7 @@ go_base_syscall_files = \
go/syscall/syscall_errno.go \ go/syscall/syscall_errno.go \
go/syscall/libcall_support.go \ go/syscall/libcall_support.go \
go/syscall/libcall_posix.go \ go/syscall/libcall_posix.go \
go/syscall/race0.go \
go/syscall/socket.go \ go/syscall/socket.go \
go/syscall/sockcmsg_unix.go \ go/syscall/sockcmsg_unix.go \
go/syscall/str.go \ go/syscall/str.go \
@ -1714,6 +1791,9 @@ libgo_go_objs = \
encoding/xml.lo \ encoding/xml.lo \
exp/ebnf.lo \ exp/ebnf.lo \
exp/html.lo \ exp/html.lo \
exp/html/atom.lo \
exp/locale/collate.lo \
exp/locale/collate/build.lo \
exp/norm.lo \ exp/norm.lo \
exp/proxy.lo \ exp/proxy.lo \
exp/terminal.lo \ exp/terminal.lo \
@ -2562,6 +2642,33 @@ exp/html/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: exp/html/check .PHONY: exp/html/check
@go_include@ exp/html/atom.lo.dep
exp/html/atom.lo.dep: $(go_exp_html_atom_files)
$(BUILDDEPS)
exp/html/atom.lo: $(go_exp_html_atom_files)
$(BUILDPACKAGE)
exp/html/atom/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/html/atom/check
@go_include@ exp/locale/collate.lo.dep
exp/locale/collate.lo.dep: $(go_exp_locale_collate_files)
$(BUILDDEPS)
exp/locale/collate.lo: $(go_exp_locale_collate_files)
$(BUILDPACKAGE)
exp/locale/collate/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/locale/collate/check
@go_include@ exp/locale/collate/build.lo.dep
exp/locale/collate/build.lo.dep: $(go_exp_locale_collate_build_files)
$(BUILDDEPS)
exp/locale/collate/build.lo: $(go_exp_locale_collate_build_files)
$(BUILDPACKAGE)
exp/locale/collate/build/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/locale/collate/build/check
@go_include@ exp/norm.lo.dep @go_include@ exp/norm.lo.dep
exp/norm.lo.dep: $(go_exp_norm_files) exp/norm.lo.dep: $(go_exp_norm_files)
$(BUILDDEPS) $(BUILDDEPS)
@ -3142,6 +3249,9 @@ syscall/signame.lo: go/syscall/signame.c
syscall/wait.lo: go/syscall/wait.c syscall/wait.lo: go/syscall/wait.c
@$(MKDIR_P) syscall @$(MKDIR_P) syscall
$(LTCOMPILE) -c -o $@ $< $(LTCOMPILE) -c -o $@ $<
syscall/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: syscall/check
# How to build a .gox file from a .lo file. # How to build a .gox file from a .lo file.
BUILDGOX = \ BUILDGOX = \
@ -3310,8 +3420,14 @@ exp/ebnf.gox: exp/ebnf.lo
$(BUILDGOX) $(BUILDGOX)
exp/html.gox: exp/html.lo exp/html.gox: exp/html.lo
$(BUILDGOX) $(BUILDGOX)
exp/html/atom.gox: exp/html/atom.lo
$(BUILDGOX)
exp/inotify.gox: exp/inotify.lo exp/inotify.gox: exp/inotify.lo
$(BUILDGOX) $(BUILDGOX)
exp/locale/collate.gox: exp/locale/collate.lo
$(BUILDGOX)
exp/locale/collate/build.gox: exp/locale/collate/build.lo
$(BUILDGOX)
exp/norm.gox: exp/norm.lo exp/norm.gox: exp/norm.lo
$(BUILDGOX) $(BUILDGOX)
exp/proxy.gox: exp/proxy.lo exp/proxy.gox: exp/proxy.lo
@ -3484,6 +3600,7 @@ TEST_PACKAGES = \
strconv/check \ strconv/check \
strings/check \ strings/check \
sync/check \ sync/check \
syscall/check \
time/check \ time/check \
unicode/check \ unicode/check \
archive/tar/check \ archive/tar/check \
@ -3532,10 +3649,14 @@ TEST_PACKAGES = \
encoding/xml/check \ encoding/xml/check \
exp/ebnf/check \ exp/ebnf/check \
exp/html/check \ exp/html/check \
exp/html/atom/check \
$(exp_inotify_check) \ $(exp_inotify_check) \
exp/locale/collate/check \
exp/locale/collate/build/check \
exp/norm/check \ exp/norm/check \
exp/proxy/check \ exp/proxy/check \
exp/terminal/check \ exp/terminal/check \
exp/types/check \
exp/utf8string/check \ exp/utf8string/check \
html/template/check \ html/template/check \
go/ast/check \ go/ast/check \

View File

@ -101,6 +101,9 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \
"$(DESTDIR)$(toolexeclibgodebugdir)" \ "$(DESTDIR)$(toolexeclibgodebugdir)" \
"$(DESTDIR)$(toolexeclibgoencodingdir)" \ "$(DESTDIR)$(toolexeclibgoencodingdir)" \
"$(DESTDIR)$(toolexeclibgoexpdir)" \ "$(DESTDIR)$(toolexeclibgoexpdir)" \
"$(DESTDIR)$(toolexeclibgoexphtmldir)" \
"$(DESTDIR)$(toolexeclibgoexplocaledir)" \
"$(DESTDIR)$(toolexeclibgoexplocalecollatedir)" \
"$(DESTDIR)$(toolexeclibgogodir)" \ "$(DESTDIR)$(toolexeclibgogodir)" \
"$(DESTDIR)$(toolexeclibgohashdir)" \ "$(DESTDIR)$(toolexeclibgohashdir)" \
"$(DESTDIR)$(toolexeclibgohtmldir)" \ "$(DESTDIR)$(toolexeclibgohtmldir)" \
@ -151,24 +154,25 @@ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \
encoding/base32.lo encoding/base64.lo encoding/binary.lo \ encoding/base32.lo encoding/base64.lo encoding/binary.lo \
encoding/csv.lo encoding/gob.lo encoding/hex.lo \ encoding/csv.lo encoding/gob.lo encoding/hex.lo \
encoding/json.lo encoding/pem.lo encoding/xml.lo exp/ebnf.lo \ encoding/json.lo encoding/pem.lo encoding/xml.lo exp/ebnf.lo \
exp/html.lo exp/norm.lo exp/proxy.lo exp/terminal.lo \ exp/html.lo exp/html/atom.lo exp/locale/collate.lo \
exp/types.lo exp/utf8string.lo html/template.lo go/ast.lo \ exp/locale/collate/build.lo exp/norm.lo exp/proxy.lo \
go/build.lo go/doc.lo go/parser.lo go/printer.lo go/scanner.lo \ exp/terminal.lo exp/types.lo exp/utf8string.lo \
go/token.lo hash/adler32.lo hash/crc32.lo hash/crc64.lo \ html/template.lo go/ast.lo go/build.lo go/doc.lo go/parser.lo \
hash/fnv.lo net/http/cgi.lo net/http/fcgi.lo \ go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \
net/http/httptest.lo net/http/httputil.lo net/http/pprof.lo \ hash/crc32.lo hash/crc64.lo hash/fnv.lo net/http/cgi.lo \
image/color.lo image/draw.lo image/gif.lo image/jpeg.lo \ net/http/fcgi.lo net/http/httptest.lo net/http/httputil.lo \
image/png.lo index/suffixarray.lo io/ioutil.lo log/syslog.lo \ net/http/pprof.lo image/color.lo image/draw.lo image/gif.lo \
log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \ image/jpeg.lo image/png.lo index/suffixarray.lo io/ioutil.lo \
mime/multipart.lo net/http.lo net/mail.lo net/rpc.lo \ log/syslog.lo log/syslog/syslog_c.lo math/big.lo math/cmplx.lo \
net/smtp.lo net/textproto.lo net/url.lo old/netchan.lo \ math/rand.lo mime/multipart.lo net/http.lo net/mail.lo \
old/regexp.lo old/template.lo os/exec.lo $(am__DEPENDENCIES_1) \ net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \
os/signal.lo os/user.lo path/filepath.lo regexp/syntax.lo \ old/netchan.lo old/regexp.lo old/template.lo os/exec.lo \
net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \ $(am__DEPENDENCIES_1) os/signal.lo os/user.lo path/filepath.lo \
sync/atomic.lo sync/atomic_c.lo text/scanner.lo \ regexp/syntax.lo net/rpc/jsonrpc.lo runtime/debug.lo \
text/tabwriter.lo text/template.lo text/template/parse.lo \ runtime/pprof.lo sync/atomic.lo sync/atomic_c.lo \
testing/iotest.lo testing/quick.lo unicode/utf16.lo \ text/scanner.lo text/tabwriter.lo text/template.lo \
unicode/utf8.lo text/template/parse.lo testing/iotest.lo testing/quick.lo \
unicode/utf16.lo unicode/utf8.lo
libgo_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ libgo_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \
../libbacktrace/libbacktrace.la $(am__DEPENDENCIES_1) \ ../libbacktrace/libbacktrace.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
@ -207,12 +211,12 @@ am__objects_5 = go-append.lo go-assert.lo go-assert-interface.lo \
go-type-identity.lo go-type-interface.lo go-type-string.lo \ go-type-identity.lo go-type-interface.lo go-type-string.lo \
go-typedesc-equal.lo go-typestring.lo go-unsafe-new.lo \ go-typedesc-equal.lo go-typestring.lo go-unsafe-new.lo \
go-unsafe-newarray.lo go-unsafe-pointer.lo go-unwind.lo \ go-unsafe-newarray.lo go-unsafe-pointer.lo go-unwind.lo \
chan.lo cpuprof.lo $(am__objects_1) mcache.lo mcentral.lo \ chan.lo cpuprof.lo lfstack.lo $(am__objects_1) mcache.lo \
$(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo mheap.lo \ mcentral.lo $(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo \
msize.lo print.lo proc.lo runtime.lo signal_unix.lo thread.lo \ mheap.lo msize.lo panic.lo parfor.lo print.lo proc.lo \
yield.lo $(am__objects_3) iface.lo malloc.lo map.lo mprof.lo \ runtime.lo signal_unix.lo thread.lo yield.lo $(am__objects_3) \
reflect.lo runtime1.lo sema.lo sigqueue.lo string.lo time.lo \ iface.lo malloc.lo map.lo mprof.lo reflect.lo runtime1.lo \
$(am__objects_4) sema.lo sigqueue.lo string.lo time.lo $(am__objects_4)
am_libgo_la_OBJECTS = $(am__objects_5) am_libgo_la_OBJECTS = $(am__objects_5)
libgo_la_OBJECTS = $(am_libgo_la_OBJECTS) libgo_la_OBJECTS = $(am_libgo_la_OBJECTS)
libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
@ -250,7 +254,9 @@ DATA = $(toolexeclibgo_DATA) $(toolexeclibgoarchive_DATA) \
$(toolexeclibgocrypto_DATA) $(toolexeclibgocryptox509_DATA) \ $(toolexeclibgocrypto_DATA) $(toolexeclibgocryptox509_DATA) \
$(toolexeclibgodatabase_DATA) $(toolexeclibgodatabasesql_DATA) \ $(toolexeclibgodatabase_DATA) $(toolexeclibgodatabasesql_DATA) \
$(toolexeclibgodebug_DATA) $(toolexeclibgoencoding_DATA) \ $(toolexeclibgodebug_DATA) $(toolexeclibgoencoding_DATA) \
$(toolexeclibgoexp_DATA) $(toolexeclibgogo_DATA) \ $(toolexeclibgoexp_DATA) $(toolexeclibgoexphtml_DATA) \
$(toolexeclibgoexplocale_DATA) \
$(toolexeclibgoexplocalecollate_DATA) $(toolexeclibgogo_DATA) \
$(toolexeclibgohash_DATA) $(toolexeclibgohtml_DATA) \ $(toolexeclibgohash_DATA) $(toolexeclibgohtml_DATA) \
$(toolexeclibgoimage_DATA) $(toolexeclibgoindex_DATA) \ $(toolexeclibgoimage_DATA) $(toolexeclibgoindex_DATA) \
$(toolexeclibgoio_DATA) $(toolexeclibgolog_DATA) \ $(toolexeclibgoio_DATA) $(toolexeclibgolog_DATA) \
@ -611,6 +617,18 @@ toolexeclibgoexp_DATA = \
exp/types.gox \ exp/types.gox \
exp/utf8string.gox exp/utf8string.gox
toolexeclibgoexphtmldir = $(toolexeclibgoexpdir)/html
toolexeclibgoexphtml_DATA = \
exp/html/atom.gox
toolexeclibgoexplocaledir = $(toolexeclibgoexpdir)/locale
toolexeclibgoexplocale_DATA = \
exp/locale/collate.gox
toolexeclibgoexplocalecollatedir = $(toolexeclibgoexplocaledir)/collate
toolexeclibgoexplocalecollate_DATA = \
exp/locale/collate/build.gox
toolexeclibgogodir = $(toolexeclibgodir)/go toolexeclibgogodir = $(toolexeclibgodir)/go
toolexeclibgogo_DATA = \ toolexeclibgogo_DATA = \
go/ast.gox \ go/ast.gox \
@ -811,6 +829,7 @@ runtime_files = \
runtime/go-unwind.c \ runtime/go-unwind.c \
runtime/chan.c \ runtime/chan.c \
runtime/cpuprof.c \ runtime/cpuprof.c \
runtime/lfstack.c \
$(runtime_lock_files) \ $(runtime_lock_files) \
runtime/mcache.c \ runtime/mcache.c \
runtime/mcentral.c \ runtime/mcentral.c \
@ -820,6 +839,8 @@ runtime_files = \
runtime/mgc0.c \ runtime/mgc0.c \
runtime/mheap.c \ runtime/mheap.c \
runtime/msize.c \ runtime/msize.c \
runtime/panic.c \
runtime/parfor.c \
runtime/print.c \ runtime/print.c \
runtime/proc.c \ runtime/proc.c \
runtime/runtime.c \ runtime/runtime.c \
@ -947,31 +968,36 @@ go_mime_files = \
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_netbsd.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_netbsd.go
@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_linux.go @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_linux.go
@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 = go/net/newpollserver.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver_unix.go
@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver_unix.go
@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver_unix.go
@LIBGO_IS_RTEMS_TRUE@go_net_newpollserver_file = go/net/newpollserver_rtems.go @LIBGO_IS_RTEMS_TRUE@go_net_newpollserver_file = go/net/newpollserver_rtems.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_bsd.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_netbsd.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_bsd.go @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_bsd.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_cgo_file = go/net/cgo_linux.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_cgo_file = go/net/cgo_linux.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_cgo_file = go/net/cgo_linux.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_cgo_file = go/net/cgo_linux.go
@LIBGO_IS_LINUX_TRUE@go_net_cgo_file = go/net/cgo_linux.go @LIBGO_IS_LINUX_TRUE@go_net_cgo_file = go/net/cgo_linux.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sock_file = go/net/sock_bsd.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sock_file = go/net/sock_solaris.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sock_file = go/net/sock_solaris.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sock_file = go/net/sock_linux.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sock_file = go/net/sock_linux.go
@LIBGO_IS_LINUX_TRUE@go_net_sock_file = go/net/sock_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sock_file = go/net/sock_linux.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockopt_file = go/net/sockopt_bsd.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockopt_file = go/net/sockopt_bsd.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockopt_file = go/net/sockopt_linux.go @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockopt_file = go/net/sockopt_linux.go
@LIBGO_IS_LINUX_TRUE@go_net_sockopt_file = go/net/sockopt_linux.go @LIBGO_IS_LINUX_TRUE@go_net_sockopt_file = go/net/sockopt_linux.go
@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_netbsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_freebsd.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@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_solaris.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_solaris.go
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockoptip_file = go/net/sockoptip_linux.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 @LIBGO_IS_LINUX_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.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_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@go_net_interface_file = go/net/interface_stub.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
@ -982,13 +1008,12 @@ go_net_files = \
go/net/dial.go \ go/net/dial.go \
go/net/dnsclient.go \ go/net/dnsclient.go \
go/net/dnsclient_unix.go \ go/net/dnsclient_unix.go \
go/net/dnsconfig.go \ go/net/dnsconfig_unix.go \
go/net/dnsmsg.go \ go/net/dnsmsg.go \
go/net/doc.go \
$(go_net_newpollserver_file) \ $(go_net_newpollserver_file) \
go/net/fd.go \ go/net/fd_unix.go \
$(go_net_fd_os_file) \ $(go_net_fd_os_file) \
go/net/file.go \ go/net/file_unix.go \
go/net/hosts.go \ go/net/hosts.go \
go/net/interface.go \ go/net/interface.go \
$(go_net_interface_file) \ $(go_net_interface_file) \
@ -997,6 +1022,7 @@ go_net_files = \
go/net/iprawsock_posix.go \ go/net/iprawsock_posix.go \
go/net/ipsock.go \ go/net/ipsock.go \
go/net/ipsock_posix.go \ go/net/ipsock_posix.go \
go/net/lookup.go \
go/net/lookup_unix.go \ go/net/lookup_unix.go \
go/net/mac.go \ go/net/mac.go \
go/net/net.go \ go/net/net.go \
@ -1004,12 +1030,12 @@ go_net_files = \
go/net/parse.go \ go/net/parse.go \
go/net/pipe.go \ go/net/pipe.go \
go/net/port.go \ go/net/port.go \
go/net/port_unix.go \
$(go_net_sendfile_file) \ $(go_net_sendfile_file) \
go/net/sock.go \ go/net/sock_posix.go \
$(go_net_sock_file) \ $(go_net_sock_file) \
go/net/sockopt.go \ go/net/sockopt_posix.go \
$(go_net_sockopt_file) \ $(go_net_sockopt_file) \
go/net/sockoptip.go \
$(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 \
@ -1058,6 +1084,7 @@ go_path_files = \
go_reflect_files = \ go_reflect_files = \
go/reflect/deepequal.go \ go/reflect/deepequal.go \
go/reflect/makefunc.go \
go/reflect/type.go \ go/reflect/type.go \
go/reflect/value.go go/reflect/value.go
@ -1098,12 +1125,14 @@ go_strconv_files = \
go_strings_files = \ 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/strings.go go/strings/strings.go
go_sync_files = \ go_sync_files = \
go/sync/cond.go \ go/sync/cond.go \
go/sync/mutex.go \ go/sync/mutex.go \
go/sync/once.go \ go/sync/once.go \
go/sync/race0.go \
go/sync/runtime.go \ go/sync/runtime.go \
go/sync/rwmutex.go \ go/sync/rwmutex.go \
go/sync/waitgroup.go go/sync/waitgroup.go
@ -1140,10 +1169,17 @@ go_unicode_files = \
go/unicode/letter.go \ go/unicode/letter.go \
go/unicode/tables.go go/unicode/tables.go
@LIBGO_IS_DARWIN_TRUE@archive_tar_atim_file = go/archive/tar/stat_atimespec.go
@LIBGO_IS_FREEBSD_TRUE@archive_tar_atim_file = go/archive/tar/stat_atimespec.go
@LIBGO_IS_LINUX_TRUE@archive_tar_atim_file = go/archive/tar/stat_atim.go
@LIBGO_IS_NETBSD_TRUE@archive_tar_atim_file = go/archive/tar/stat_atimespec.go
@LIBGO_IS_OPENBSD_TRUE@archive_tar_atim_file = go/archive/tar/stat_atim.go
go_archive_tar_files = \ go_archive_tar_files = \
go/archive/tar/common.go \ go/archive/tar/common.go \
go/archive/tar/reader.go \ go/archive/tar/reader.go \
go/archive/tar/writer.go go/archive/tar/stat_unix.go \
go/archive/tar/writer.go \
$(archive_tar_atim_file)
go_archive_zip_files = \ go_archive_zip_files = \
go/archive/zip/reader.go \ go/archive/zip/reader.go \
@ -1157,6 +1193,7 @@ go_compress_bzip2_files = \
go/compress/bzip2/move_to_front.go go/compress/bzip2/move_to_front.go
go_compress_flate_files = \ go_compress_flate_files = \
go/compress/flate/copy.go \
go/compress/flate/deflate.go \ go/compress/flate/deflate.go \
go/compress/flate/huffman_bit_writer.go \ go/compress/flate/huffman_bit_writer.go \
go/compress/flate/huffman_code.go \ go/compress/flate/huffman_code.go \
@ -1188,6 +1225,7 @@ go_container_ring_files = \
go_crypto_aes_files = \ go_crypto_aes_files = \
go/crypto/aes/block.go \ go/crypto/aes/block.go \
go/crypto/aes/cipher.go \ go/crypto/aes/cipher.go \
go/crypto/aes/cipher_generic.go \
go/crypto/aes/const.go go/crypto/aes/const.go
go_crypto_cipher_files = \ go_crypto_cipher_files = \
@ -1257,10 +1295,12 @@ go_crypto_tls_files = \
go/crypto/tls/handshake_server.go \ go/crypto/tls/handshake_server.go \
go/crypto/tls/key_agreement.go \ go/crypto/tls/key_agreement.go \
go/crypto/tls/prf.go \ go/crypto/tls/prf.go \
go/crypto/tls/ticket.go \
go/crypto/tls/tls.go go/crypto/tls/tls.go
go_crypto_x509_files = \ go_crypto_x509_files = \
go/crypto/x509/cert_pool.go \ go/crypto/x509/cert_pool.go \
go/crypto/x509/pem_decrypt.go \
go/crypto/x509/pkcs1.go \ go/crypto/x509/pkcs1.go \
go/crypto/x509/pkcs8.go \ go/crypto/x509/pkcs8.go \
go/crypto/x509/root.go \ go/crypto/x509/root.go \
@ -1371,9 +1411,30 @@ go_exp_html_files = \
go/exp/html/render.go \ go/exp/html/render.go \
go/exp/html/token.go go/exp/html/token.go
go_exp_html_atom_files = \
go/exp/html/atom/atom.go \
go/exp/html/atom/table.go
go_exp_inotify_files = \ go_exp_inotify_files = \
go/exp/inotify/inotify_linux.go go/exp/inotify/inotify_linux.go
go_exp_locale_collate_files = \
go/exp/locale/collate/colelem.go \
go/exp/locale/collate/collate.go \
go/exp/locale/collate/contract.go \
go/exp/locale/collate/export.go \
go/exp/locale/collate/table.go \
go/exp/locale/collate/tables.go \
go/exp/locale/collate/trie.go
go_exp_locale_collate_build_files = \
go/exp/locale/collate/build/builder.go \
go/exp/locale/collate/build/colelem.go \
go/exp/locale/collate/build/contract.go \
go/exp/locale/collate/build/order.go \
go/exp/locale/collate/build/table.go \
go/exp/locale/collate/build/trie.go
go_exp_norm_files = \ go_exp_norm_files = \
go/exp/norm/composition.go \ go/exp/norm/composition.go \
go/exp/norm/forminfo.go \ go/exp/norm/forminfo.go \
@ -1407,6 +1468,7 @@ go_exp_utf8string_files = \
go_go_ast_files = \ go_go_ast_files = \
go/go/ast/ast.go \ go/go/ast/ast.go \
go/go/ast/commentmap.go \
go/go/ast/filter.go \ go/go/ast/filter.go \
go/go/ast/import.go \ go/go/ast/import.go \
go/go/ast/print.go \ go/go/ast/print.go \
@ -1417,6 +1479,7 @@ go_go_ast_files = \
go_go_build_files = \ go_go_build_files = \
go/go/build/build.go \ go/go/build/build.go \
go/go/build/doc.go \ go/go/build/doc.go \
go/go/build/read.go \
syslist.go syslist.go
go_go_doc_files = \ go_go_doc_files = \
@ -1490,6 +1553,7 @@ go_image_jpeg_files = \
go/image/jpeg/writer.go go/image/jpeg/writer.go
go_image_png_files = \ go_image_png_files = \
go/image/png/paeth.go \
go/image/png/reader.go \ go/image/png/reader.go \
go/image/png/writer.go go/image/png/writer.go
@ -1498,6 +1562,7 @@ go_index_suffixarray_files = \
go/index/suffixarray/suffixarray.go go/index/suffixarray/suffixarray.go
go_io_ioutil_files = \ go_io_ioutil_files = \
go/io/ioutil/blackhole.go \
go/io/ioutil/ioutil.go \ go/io/ioutil/ioutil.go \
go/io/ioutil/tempfile.go go/io/ioutil/tempfile.go
@ -1624,6 +1689,7 @@ go_path_filepath_files = \
go_regexp_syntax_files = \ go_regexp_syntax_files = \
go/regexp/syntax/compile.go \ go/regexp/syntax/compile.go \
go/regexp/syntax/doc.go \
go/regexp/syntax/parse.go \ go/regexp/syntax/parse.go \
go/regexp/syntax/perl_groups.go \ go/regexp/syntax/perl_groups.go \
go/regexp/syntax/prog.go \ go/regexp/syntax/prog.go \
@ -1750,6 +1816,7 @@ go_base_syscall_files = \
go/syscall/syscall_errno.go \ go/syscall/syscall_errno.go \
go/syscall/libcall_support.go \ go/syscall/libcall_support.go \
go/syscall/libcall_posix.go \ go/syscall/libcall_posix.go \
go/syscall/race0.go \
go/syscall/socket.go \ go/syscall/socket.go \
go/syscall/sockcmsg_unix.go \ go/syscall/sockcmsg_unix.go \
go/syscall/str.go \ go/syscall/str.go \
@ -1867,6 +1934,9 @@ libgo_go_objs = \
encoding/xml.lo \ encoding/xml.lo \
exp/ebnf.lo \ exp/ebnf.lo \
exp/html.lo \ exp/html.lo \
exp/html/atom.lo \
exp/locale/collate.lo \
exp/locale/collate/build.lo \
exp/norm.lo \ exp/norm.lo \
exp/proxy.lo \ exp/proxy.lo \
exp/terminal.lo \ exp/terminal.lo \
@ -2065,6 +2135,7 @@ TEST_PACKAGES = \
strconv/check \ strconv/check \
strings/check \ strings/check \
sync/check \ sync/check \
syscall/check \
time/check \ time/check \
unicode/check \ unicode/check \
archive/tar/check \ archive/tar/check \
@ -2113,10 +2184,14 @@ TEST_PACKAGES = \
encoding/xml/check \ encoding/xml/check \
exp/ebnf/check \ exp/ebnf/check \
exp/html/check \ exp/html/check \
exp/html/atom/check \
$(exp_inotify_check) \ $(exp_inotify_check) \
exp/locale/collate/check \
exp/locale/collate/build/check \
exp/norm/check \ exp/norm/check \
exp/proxy/check \ exp/proxy/check \
exp/terminal/check \ exp/terminal/check \
exp/types/check \
exp/utf8string/check \ exp/utf8string/check \
html/template/check \ html/template/check \
go/ast/check \ go/ast/check \
@ -2377,6 +2452,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsafe-pointer.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsafe-pointer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unwind.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unwind.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iface.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lfstack.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock_futex.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock_futex.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock_sema.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock_sema.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/malloc.Plo@am__quote@
@ -2391,6 +2467,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mheap.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mheap.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mprof.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mprof.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/msize.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/msize.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/panic.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parfor.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reflect.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reflect.Plo@am__quote@
@ -2897,6 +2975,13 @@ cpuprof.lo: runtime/cpuprof.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cpuprof.lo `test -f 'runtime/cpuprof.c' || echo '$(srcdir)/'`runtime/cpuprof.c @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o cpuprof.lo `test -f 'runtime/cpuprof.c' || echo '$(srcdir)/'`runtime/cpuprof.c
lfstack.lo: runtime/lfstack.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lfstack.lo -MD -MP -MF $(DEPDIR)/lfstack.Tpo -c -o lfstack.lo `test -f 'runtime/lfstack.c' || echo '$(srcdir)/'`runtime/lfstack.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/lfstack.Tpo $(DEPDIR)/lfstack.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/lfstack.c' object='lfstack.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lfstack.lo `test -f 'runtime/lfstack.c' || echo '$(srcdir)/'`runtime/lfstack.c
lock_sema.lo: runtime/lock_sema.c lock_sema.lo: runtime/lock_sema.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lock_sema.lo -MD -MP -MF $(DEPDIR)/lock_sema.Tpo -c -o lock_sema.lo `test -f 'runtime/lock_sema.c' || echo '$(srcdir)/'`runtime/lock_sema.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lock_sema.lo -MD -MP -MF $(DEPDIR)/lock_sema.Tpo -c -o lock_sema.lo `test -f 'runtime/lock_sema.c' || echo '$(srcdir)/'`runtime/lock_sema.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/lock_sema.Tpo $(DEPDIR)/lock_sema.Plo @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/lock_sema.Tpo $(DEPDIR)/lock_sema.Plo
@ -2988,6 +3073,20 @@ msize.lo: runtime/msize.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msize.lo `test -f 'runtime/msize.c' || echo '$(srcdir)/'`runtime/msize.c @am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o msize.lo `test -f 'runtime/msize.c' || echo '$(srcdir)/'`runtime/msize.c
panic.lo: runtime/panic.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT panic.lo -MD -MP -MF $(DEPDIR)/panic.Tpo -c -o panic.lo `test -f 'runtime/panic.c' || echo '$(srcdir)/'`runtime/panic.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/panic.Tpo $(DEPDIR)/panic.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/panic.c' object='panic.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o panic.lo `test -f 'runtime/panic.c' || echo '$(srcdir)/'`runtime/panic.c
parfor.lo: runtime/parfor.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT parfor.lo -MD -MP -MF $(DEPDIR)/parfor.Tpo -c -o parfor.lo `test -f 'runtime/parfor.c' || echo '$(srcdir)/'`runtime/parfor.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/parfor.Tpo $(DEPDIR)/parfor.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='runtime/parfor.c' object='parfor.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o parfor.lo `test -f 'runtime/parfor.c' || echo '$(srcdir)/'`runtime/parfor.c
print.lo: runtime/print.c print.lo: runtime/print.c
@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT print.lo -MD -MP -MF $(DEPDIR)/print.Tpo -c -o print.lo `test -f 'runtime/print.c' || echo '$(srcdir)/'`runtime/print.c @am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT print.lo -MD -MP -MF $(DEPDIR)/print.Tpo -c -o print.lo `test -f 'runtime/print.c' || echo '$(srcdir)/'`runtime/print.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/print.Tpo $(DEPDIR)/print.Plo @am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/print.Tpo $(DEPDIR)/print.Plo
@ -3317,6 +3416,66 @@ uninstall-toolexeclibgoexpDATA:
test -n "$$files" || exit 0; \ test -n "$$files" || exit 0; \
echo " ( cd '$(DESTDIR)$(toolexeclibgoexpdir)' && rm -f" $$files ")"; \ echo " ( cd '$(DESTDIR)$(toolexeclibgoexpdir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(toolexeclibgoexpdir)" && rm -f $$files cd "$(DESTDIR)$(toolexeclibgoexpdir)" && rm -f $$files
install-toolexeclibgoexphtmlDATA: $(toolexeclibgoexphtml_DATA)
@$(NORMAL_INSTALL)
test -z "$(toolexeclibgoexphtmldir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoexphtmldir)"
@list='$(toolexeclibgoexphtml_DATA)'; test -n "$(toolexeclibgoexphtmldir)" || 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)$(toolexeclibgoexphtmldir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(toolexeclibgoexphtmldir)" || exit $$?; \
done
uninstall-toolexeclibgoexphtmlDATA:
@$(NORMAL_UNINSTALL)
@list='$(toolexeclibgoexphtml_DATA)'; test -n "$(toolexeclibgoexphtmldir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
test -n "$$files" || exit 0; \
echo " ( cd '$(DESTDIR)$(toolexeclibgoexphtmldir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(toolexeclibgoexphtmldir)" && rm -f $$files
install-toolexeclibgoexplocaleDATA: $(toolexeclibgoexplocale_DATA)
@$(NORMAL_INSTALL)
test -z "$(toolexeclibgoexplocaledir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoexplocaledir)"
@list='$(toolexeclibgoexplocale_DATA)'; test -n "$(toolexeclibgoexplocaledir)" || 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)$(toolexeclibgoexplocaledir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(toolexeclibgoexplocaledir)" || exit $$?; \
done
uninstall-toolexeclibgoexplocaleDATA:
@$(NORMAL_UNINSTALL)
@list='$(toolexeclibgoexplocale_DATA)'; test -n "$(toolexeclibgoexplocaledir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
test -n "$$files" || exit 0; \
echo " ( cd '$(DESTDIR)$(toolexeclibgoexplocaledir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(toolexeclibgoexplocaledir)" && rm -f $$files
install-toolexeclibgoexplocalecollateDATA: $(toolexeclibgoexplocalecollate_DATA)
@$(NORMAL_INSTALL)
test -z "$(toolexeclibgoexplocalecollatedir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoexplocalecollatedir)"
@list='$(toolexeclibgoexplocalecollate_DATA)'; test -n "$(toolexeclibgoexplocalecollatedir)" || 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)$(toolexeclibgoexplocalecollatedir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(toolexeclibgoexplocalecollatedir)" || exit $$?; \
done
uninstall-toolexeclibgoexplocalecollateDATA:
@$(NORMAL_UNINSTALL)
@list='$(toolexeclibgoexplocalecollate_DATA)'; test -n "$(toolexeclibgoexplocalecollatedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
test -n "$$files" || exit 0; \
echo " ( cd '$(DESTDIR)$(toolexeclibgoexplocalecollatedir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(toolexeclibgoexplocalecollatedir)" && rm -f $$files
install-toolexeclibgogoDATA: $(toolexeclibgogo_DATA) install-toolexeclibgogoDATA: $(toolexeclibgogo_DATA)
@$(NORMAL_INSTALL) @$(NORMAL_INSTALL)
test -z "$(toolexeclibgogodir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgogodir)" test -z "$(toolexeclibgogodir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgogodir)"
@ -3898,7 +4057,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)$(toolexeclibgoexphtmldir)" "$(DESTDIR)$(toolexeclibgoexplocaledir)" "$(DESTDIR)$(toolexeclibgoexplocalecollatedir)" "$(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 \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done done
install: install-recursive install: install-recursive
@ -3969,6 +4128,9 @@ install-exec-am: install-multi install-toolexeclibLIBRARIES \
install-toolexeclibgodatabasesqlDATA \ install-toolexeclibgodatabasesqlDATA \
install-toolexeclibgodebugDATA \ install-toolexeclibgodebugDATA \
install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \ install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
install-toolexeclibgoexphtmlDATA \
install-toolexeclibgoexplocaleDATA \
install-toolexeclibgoexplocalecollateDATA \
install-toolexeclibgogoDATA install-toolexeclibgohashDATA \ install-toolexeclibgogoDATA install-toolexeclibgohashDATA \
install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \ install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \
install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \ install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \
@ -4034,8 +4196,11 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgodatabasesqlDATA \ uninstall-toolexeclibgodatabasesqlDATA \
uninstall-toolexeclibgodebugDATA \ uninstall-toolexeclibgodebugDATA \
uninstall-toolexeclibgoencodingDATA \ uninstall-toolexeclibgoencodingDATA \
uninstall-toolexeclibgoexpDATA uninstall-toolexeclibgogoDATA \ uninstall-toolexeclibgoexpDATA \
uninstall-toolexeclibgohashDATA \ uninstall-toolexeclibgoexphtmlDATA \
uninstall-toolexeclibgoexplocaleDATA \
uninstall-toolexeclibgoexplocalecollateDATA \
uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \ uninstall-toolexeclibgohtmlDATA \
uninstall-toolexeclibgoimageDATA \ uninstall-toolexeclibgoimageDATA \
uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \ uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \
@ -4080,6 +4245,9 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
install-toolexeclibgodatabasesqlDATA \ install-toolexeclibgodatabasesqlDATA \
install-toolexeclibgodebugDATA \ install-toolexeclibgodebugDATA \
install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \ install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
install-toolexeclibgoexphtmlDATA \
install-toolexeclibgoexplocaleDATA \
install-toolexeclibgoexplocalecollateDATA \
install-toolexeclibgogoDATA install-toolexeclibgohashDATA \ install-toolexeclibgogoDATA install-toolexeclibgohashDATA \
install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \ install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \
install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \ install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \
@ -4109,8 +4277,11 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
uninstall-toolexeclibgodatabasesqlDATA \ uninstall-toolexeclibgodatabasesqlDATA \
uninstall-toolexeclibgodebugDATA \ uninstall-toolexeclibgodebugDATA \
uninstall-toolexeclibgoencodingDATA \ uninstall-toolexeclibgoencodingDATA \
uninstall-toolexeclibgoexpDATA uninstall-toolexeclibgogoDATA \ uninstall-toolexeclibgoexpDATA \
uninstall-toolexeclibgohashDATA \ uninstall-toolexeclibgoexphtmlDATA \
uninstall-toolexeclibgoexplocaleDATA \
uninstall-toolexeclibgoexplocalecollateDATA \
uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \ uninstall-toolexeclibgohtmlDATA \
uninstall-toolexeclibgoimageDATA \ uninstall-toolexeclibgoimageDATA \
uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \ uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \
@ -4909,6 +5080,33 @@ exp/html/check: $(CHECK_DEPS)
@$(CHECK) @$(CHECK)
.PHONY: exp/html/check .PHONY: exp/html/check
@go_include@ exp/html/atom.lo.dep
exp/html/atom.lo.dep: $(go_exp_html_atom_files)
$(BUILDDEPS)
exp/html/atom.lo: $(go_exp_html_atom_files)
$(BUILDPACKAGE)
exp/html/atom/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/html/atom/check
@go_include@ exp/locale/collate.lo.dep
exp/locale/collate.lo.dep: $(go_exp_locale_collate_files)
$(BUILDDEPS)
exp/locale/collate.lo: $(go_exp_locale_collate_files)
$(BUILDPACKAGE)
exp/locale/collate/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/locale/collate/check
@go_include@ exp/locale/collate/build.lo.dep
exp/locale/collate/build.lo.dep: $(go_exp_locale_collate_build_files)
$(BUILDDEPS)
exp/locale/collate/build.lo: $(go_exp_locale_collate_build_files)
$(BUILDPACKAGE)
exp/locale/collate/build/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: exp/locale/collate/build/check
@go_include@ exp/norm.lo.dep @go_include@ exp/norm.lo.dep
exp/norm.lo.dep: $(go_exp_norm_files) exp/norm.lo.dep: $(go_exp_norm_files)
$(BUILDDEPS) $(BUILDDEPS)
@ -5486,6 +5684,9 @@ syscall/signame.lo: go/syscall/signame.c
syscall/wait.lo: go/syscall/wait.c syscall/wait.lo: go/syscall/wait.c
@$(MKDIR_P) syscall @$(MKDIR_P) syscall
$(LTCOMPILE) -c -o $@ $< $(LTCOMPILE) -c -o $@ $<
syscall/check: $(CHECK_DEPS)
@$(CHECK)
.PHONY: syscall/check
bufio.gox: bufio.lo bufio.gox: bufio.lo
$(BUILDGOX) $(BUILDGOX)
@ -5649,8 +5850,14 @@ exp/ebnf.gox: exp/ebnf.lo
$(BUILDGOX) $(BUILDGOX)
exp/html.gox: exp/html.lo exp/html.gox: exp/html.lo
$(BUILDGOX) $(BUILDGOX)
exp/html/atom.gox: exp/html/atom.lo
$(BUILDGOX)
exp/inotify.gox: exp/inotify.lo exp/inotify.gox: exp/inotify.lo
$(BUILDGOX) $(BUILDGOX)
exp/locale/collate.gox: exp/locale/collate.lo
$(BUILDGOX)
exp/locale/collate/build.gox: exp/locale/collate/build.lo
$(BUILDGOX)
exp/norm.gox: exp/norm.lo exp/norm.gox: exp/norm.lo
$(BUILDGOX) $(BUILDGOX)
exp/proxy.gox: exp/proxy.lo exp/proxy.gox: exp/proxy.lo

20
libgo/configure vendored
View File

@ -658,6 +658,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_OPENBSD_FALSE
LIBGO_IS_OPENBSD_TRUE
LIBGO_IS_NETBSD_FALSE LIBGO_IS_NETBSD_FALSE
LIBGO_IS_NETBSD_TRUE LIBGO_IS_NETBSD_TRUE
LIBGO_IS_LINUX_FALSE LIBGO_IS_LINUX_FALSE
@ -11100,7 +11102,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 11103 "configure" #line 11105 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@ -11206,7 +11208,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 11209 "configure" #line 11211 "configure"
#include "confdefs.h" #include "confdefs.h"
#if HAVE_DLFCN_H #if HAVE_DLFCN_H
@ -13471,6 +13473,7 @@ is_freebsd=no
is_irix=no is_irix=no
is_linux=no is_linux=no
is_netbsd=no is_netbsd=no
is_openbsd=no
is_rtems=no is_rtems=no
is_solaris=no is_solaris=no
GOOS=unknown GOOS=unknown
@ -13480,6 +13483,7 @@ case ${host} in
*-*-irix6*) is_irix=yes; GOOS=irix ;; *-*-irix6*) is_irix=yes; GOOS=irix ;;
*-*-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 ;;
*-*-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
@ -13523,6 +13527,14 @@ else
LIBGO_IS_NETBSD_FALSE= LIBGO_IS_NETBSD_FALSE=
fi fi
if test $is_openbsd = yes; then
LIBGO_IS_OPENBSD_TRUE=
LIBGO_IS_OPENBSD_FALSE='#'
else
LIBGO_IS_OPENBSD_TRUE='#'
LIBGO_IS_OPENBSD_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='#'
@ -15311,6 +15323,10 @@ if test -z "${LIBGO_IS_NETBSD_TRUE}" && test -z "${LIBGO_IS_NETBSD_FALSE}"; then
as_fn_error "conditional \"LIBGO_IS_NETBSD\" was never defined. as_fn_error "conditional \"LIBGO_IS_NETBSD\" 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_OPENBSD_TRUE}" && test -z "${LIBGO_IS_OPENBSD_FALSE}"; then
as_fn_error "conditional \"LIBGO_IS_OPENBSD\" 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

@ -129,6 +129,7 @@ is_freebsd=no
is_irix=no is_irix=no
is_linux=no is_linux=no
is_netbsd=no is_netbsd=no
is_openbsd=no
is_rtems=no is_rtems=no
is_solaris=no is_solaris=no
GOOS=unknown GOOS=unknown
@ -138,6 +139,7 @@ case ${host} in
*-*-irix6*) is_irix=yes; GOOS=irix ;; *-*-irix6*) is_irix=yes; GOOS=irix ;;
*-*-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 ;;
*-*-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
@ -146,6 +148,7 @@ AM_CONDITIONAL(LIBGO_IS_FREEBSD, test $is_freebsd = yes)
AM_CONDITIONAL(LIBGO_IS_IRIX, test $is_irix = yes) 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_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)

View File

@ -11,7 +11,12 @@
// http://www.gnu.org/software/tar/manual/html_node/Standard.html // http://www.gnu.org/software/tar/manual/html_node/Standard.html
package tar package tar
import "time" import (
"errors"
"fmt"
"os"
"time"
)
const ( const (
blockSize = 512 blockSize = 512
@ -49,6 +54,62 @@ type Header struct {
ChangeTime time.Time // status change time ChangeTime time.Time // status change time
} }
// sysStat, if non-nil, populates h from system-dependent fields of fi.
var sysStat func(fi os.FileInfo, h *Header) error
// Mode constants from the tar spec.
const (
c_ISDIR = 040000
c_ISFIFO = 010000
c_ISREG = 0100000
c_ISLNK = 0120000
c_ISBLK = 060000
c_ISCHR = 020000
c_ISSOCK = 0140000
)
// FileInfoHeader creates a partially-populated Header from fi.
// If fi describes a symlink, FileInfoHeader records link as the link target.
func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
if fi == nil {
return nil, errors.New("tar: FileInfo is nil")
}
h := &Header{
Name: fi.Name(),
ModTime: fi.ModTime(),
Mode: int64(fi.Mode().Perm()), // or'd with c_IS* constants later
}
switch {
case fi.Mode()&os.ModeType == 0:
h.Mode |= c_ISREG
h.Typeflag = TypeReg
h.Size = fi.Size()
case fi.IsDir():
h.Typeflag = TypeDir
h.Mode |= c_ISDIR
case fi.Mode()&os.ModeSymlink != 0:
h.Typeflag = TypeSymlink
h.Mode |= c_ISLNK
h.Linkname = link
case fi.Mode()&os.ModeDevice != 0:
if fi.Mode()&os.ModeCharDevice != 0 {
h.Mode |= c_ISCHR
h.Typeflag = TypeChar
} else {
h.Mode |= c_ISBLK
h.Typeflag = TypeBlock
}
case fi.Mode()&os.ModeSocket != 0:
h.Mode |= c_ISSOCK
default:
return nil, fmt.Errorf("archive/tar: unknown file mode %v", fi.Mode())
}
if sysStat != nil {
return h, sysStat(fi, h)
}
return h, nil
}
var zeroBlock = make([]byte, blockSize) var zeroBlock = make([]byte, blockSize)
// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values. // POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.

View File

@ -0,0 +1,20 @@
// Copyright 2012 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.
// +build linux openbsd
package tar
import (
"syscall"
"time"
)
func statAtime(st *syscall.Stat_t) time.Time {
return time.Unix(st.Atim.Unix())
}
func statCtime(st *syscall.Stat_t) time.Time {
return time.Unix(st.Ctim.Unix())
}

View File

@ -0,0 +1,20 @@
// Copyright 2012 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.
// +build darwin freebsd netbsd
package tar
import (
"syscall"
"time"
)
func statAtime(st *syscall.Stat_t) time.Time {
return time.Unix(st.Atimespec.Unix())
}
func statCtime(st *syscall.Stat_t) time.Time {
return time.Unix(st.Ctimespec.Unix())
}

View File

@ -0,0 +1,32 @@
// Copyright 2012 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.
// +build linux darwin freebsd openbsd netbsd
package tar
import (
"os"
"syscall"
)
func init() {
sysStat = statUnix
}
func statUnix(fi os.FileInfo, h *Header) error {
sys, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return nil
}
h.Uid = int(sys.Uid)
h.Gid = int(sys.Gid)
// TODO(bradfitz): populate username & group. os/user
// doesn't cache LookupId lookups, and lacks group
// lookup functions.
h.AccessTime = statAtime(sys)
h.ChangeTime = statCtime(sys)
// TODO(bradfitz): major/minor device numbers?
return nil
}

View File

@ -0,0 +1,56 @@
// Copyright 2012 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 tar
import (
"os"
"testing"
"time"
)
func TestFileInfoHeader(t *testing.T) {
fi, err := os.Lstat("testdata/small.txt")
if err != nil {
t.Fatal(err)
}
h, err := FileInfoHeader(fi, "")
if err != nil {
t.Fatalf("on small.txt: %v", err)
}
if g, e := h.Name, "small.txt"; g != e {
t.Errorf("Name = %q; want %q", g, e)
}
if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e {
t.Errorf("Mode = %#o; want %#o", g, e)
}
if g, e := h.Size, int64(5); g != e {
t.Errorf("Size = %v; want %v", g, e)
}
if g, e := h.ModTime, fi.ModTime(); !g.Equal(e) {
t.Errorf("ModTime = %v; want %v", g, e)
}
}
func TestFileInfoHeaderSymlink(t *testing.T) {
h, err := FileInfoHeader(symlink{}, "some-target")
if err != nil {
t.Fatal(err)
}
if g, e := h.Name, "some-symlink"; g != e {
t.Errorf("Name = %q; want %q", g, e)
}
if g, e := h.Linkname, "some-target"; g != e {
t.Errorf("Linkname = %q; want %q", g, e)
}
}
type symlink struct{}
func (symlink) Name() string { return "some-symlink" }
func (symlink) Size() int64 { return 0 }
func (symlink) Mode() os.FileMode { return os.ModeSymlink }
func (symlink) ModTime() time.Time { return time.Time{} }
func (symlink) IsDir() bool { return false }
func (symlink) Sys() interface{} { return nil }

View File

@ -27,7 +27,7 @@ var (
// //
// Example: // Example:
// tw := tar.NewWriter(w) // tw := tar.NewWriter(w)
// hdr := new(Header) // hdr := new(tar.Header)
// hdr.Size = length of data in bytes // hdr.Size = length of data in bytes
// // populate other hdr fields as desired // // populate other hdr fields as desired
// if err := tw.WriteHeader(hdr); err != nil { // if err := tw.WriteHeader(hdr); err != nil {

View File

@ -103,7 +103,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
} }
z.File = append(z.File, f) z.File = append(z.File, f)
} }
if uint16(len(z.File)) != end.directoryRecords { if uint16(len(z.File)) != uint16(end.directoryRecords) { // only compare 16 bits here
// Return the readDirectoryHeader error if we read // Return the readDirectoryHeader error if we read
// the wrong number of directory entries. // the wrong number of directory entries.
return err return err
@ -123,7 +123,7 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
if err != nil { if err != nil {
return return
} }
size := int64(f.CompressedSize) 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 { switch f.Method {
case Store: // (no compression) case Store: // (no compression)
@ -220,6 +220,8 @@ func readDirectoryHeader(f *File, r io.Reader) error {
f.CRC32 = b.uint32() f.CRC32 = b.uint32()
f.CompressedSize = b.uint32() f.CompressedSize = b.uint32()
f.UncompressedSize = b.uint32() f.UncompressedSize = b.uint32()
f.CompressedSize64 = uint64(f.CompressedSize)
f.UncompressedSize64 = uint64(f.UncompressedSize)
filenameLen := int(b.uint16()) filenameLen := int(b.uint16())
extraLen := int(b.uint16()) extraLen := int(b.uint16())
commentLen := int(b.uint16()) commentLen := int(b.uint16())
@ -233,6 +235,28 @@ func readDirectoryHeader(f *File, r io.Reader) error {
f.Name = string(d[:filenameLen]) f.Name = string(d[:filenameLen])
f.Extra = d[filenameLen : filenameLen+extraLen] f.Extra = d[filenameLen : filenameLen+extraLen]
f.Comment = string(d[filenameLen+extraLen:]) f.Comment = string(d[filenameLen+extraLen:])
if len(f.Extra) > 0 {
b := readBuf(f.Extra)
for len(b) > 0 {
tag := b.uint16()
size := b.uint16()
if tag == zip64ExtraId {
// update directory values from the zip64 extra block
eb := readBuf(b)
if len(eb) >= 8 {
f.UncompressedSize64 = eb.uint64()
}
if len(eb) >= 8 {
f.CompressedSize64 = eb.uint64()
}
if len(eb) >= 8 {
f.headerOffset = int64(eb.uint64())
}
}
b = b[size:]
}
}
return nil return nil
} }
@ -263,15 +287,23 @@ func readDataDescriptor(r io.Reader, f *File) error {
return err return err
} }
b := readBuf(buf[:12]) b := readBuf(buf[:12])
f.CRC32 = b.uint32() if b.uint32() != f.CRC32 {
f.CompressedSize = b.uint32() return ErrChecksum
f.UncompressedSize = b.uint32() }
// The two sizes that follow here can be either 32 bits or 64 bits
// but the spec is not very clear on this and different
// interpretations has been made causing incompatibilities. We
// already have the sizes from the central directory so we can
// just ignore these.
return nil return nil
} }
func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) { func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) {
// look for directoryEndSignature in the last 1k, then in the last 65k // look for directoryEndSignature in the last 1k, then in the last 65k
var buf []byte var buf []byte
var directoryEndOffset int64
for i, bLen := range []int64{1024, 65 * 1024} { for i, bLen := range []int64{1024, 65 * 1024} {
if bLen > size { if bLen > size {
bLen = size bLen = size
@ -282,6 +314,7 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
} }
if p := findSignatureInBlock(buf); p >= 0 { if p := findSignatureInBlock(buf); p >= 0 {
buf = buf[p:] buf = buf[p:]
directoryEndOffset = size - bLen + int64(p)
break break
} }
if i == 1 || bLen == size { if i == 1 || bLen == size {
@ -292,12 +325,12 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
// read header into struct // read header into struct
b := readBuf(buf[4:]) // skip signature b := readBuf(buf[4:]) // skip signature
d := &directoryEnd{ d := &directoryEnd{
diskNbr: b.uint16(), diskNbr: uint32(b.uint16()),
dirDiskNbr: b.uint16(), dirDiskNbr: uint32(b.uint16()),
dirRecordsThisDisk: b.uint16(), dirRecordsThisDisk: uint64(b.uint16()),
directoryRecords: b.uint16(), directoryRecords: uint64(b.uint16()),
directorySize: b.uint32(), directorySize: uint64(b.uint32()),
directoryOffset: b.uint32(), directoryOffset: uint64(b.uint32()),
commentLen: b.uint16(), commentLen: b.uint16(),
} }
l := int(d.commentLen) l := int(d.commentLen)
@ -305,9 +338,62 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
return nil, errors.New("zip: invalid comment length") return nil, errors.New("zip: invalid comment length")
} }
d.comment = string(b[:l]) d.comment = string(b[:l])
p, err := findDirectory64End(r, directoryEndOffset)
if err == nil && p >= 0 {
err = readDirectory64End(r, p, d)
}
if err != nil {
return nil, err
}
return d, nil return d, nil
} }
// findDirectory64End tries to read the zip64 locator just before the
// directory end and returns the offset of the zip64 directory end if
// found.
func findDirectory64End(r io.ReaderAt, directoryEndOffset int64) (int64, error) {
locOffset := directoryEndOffset - directory64LocLen
if locOffset < 0 {
return -1, nil // no need to look for a header outside the file
}
buf := make([]byte, directory64LocLen)
if _, err := r.ReadAt(buf, locOffset); err != nil {
return -1, err
}
b := readBuf(buf)
if sig := b.uint32(); sig != directory64LocSignature {
return -1, nil
}
b = b[4:] // skip number of the disk with the start of the zip64 end of central directory
p := b.uint64() // relative offset of the zip64 end of central directory record
return int64(p), nil
}
// readDirectory64End reads the zip64 directory end and updates the
// directory end with the zip64 directory end values.
func readDirectory64End(r io.ReaderAt, offset int64, d *directoryEnd) (err error) {
buf := make([]byte, directory64EndLen)
if _, err := r.ReadAt(buf, offset); err != nil {
return err
}
b := readBuf(buf)
if sig := b.uint32(); sig != directory64EndSignature {
return ErrFormat
}
b = b[12:] // skip dir size, version and version needed (uint64 + 2x uint16)
d.diskNbr = b.uint32() // number of this disk
d.dirDiskNbr = b.uint32() // number of the disk with the start of the central directory
d.dirRecordsThisDisk = b.uint64() // total number of entries in the central directory on this disk
d.directoryRecords = b.uint64() // total number of entries in the central directory
d.directorySize = b.uint64() // size of the central directory
d.directoryOffset = b.uint64() // offset of start of central directory with respect to the starting disk number
return nil
}
func findSignatureInBlock(b []byte) int { func findSignatureInBlock(b []byte) int {
for i := len(b) - directoryEndLen; i >= 0; i-- { for i := len(b) - directoryEndLen; i >= 0; i-- {
// defined from directoryEndSignature in struct.go // defined from directoryEndSignature in struct.go
@ -335,3 +421,9 @@ func (b *readBuf) uint32() uint32 {
*b = (*b)[4:] *b = (*b)[4:]
return v return v
} }
func (b *readBuf) uint64() uint64 {
v := binary.LittleEndian.Uint64(*b)
*b = (*b)[8:]
return v
}

View File

@ -206,6 +206,17 @@ var tests = []ZipTest{
}, },
}, },
}, },
{
Name: "zip64.zip",
File: []ZipTestFile{
{
Name: "README",
Content: []byte("This small file is in ZIP64 format.\n"),
Mtime: "08-10-12 14:33:32",
Mode: 0644,
},
},
},
} }
var crossPlatform = []ZipTestFile{ var crossPlatform = []ZipTestFile{

View File

@ -7,12 +7,19 @@ Package zip provides support for reading and writing ZIP archives.
See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
This package does not support ZIP64 or disk spanning. This package does not support disk spanning.
A note about ZIP64:
To be backwards compatible the FileHeader has both 32 and 64 bit Size
fields. The 64 bit fields will always contain the correct value and
for normal archives both fields will be the same. For files requiring
the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit
fields must be used instead.
*/ */
package zip package zip
import ( import (
"errors"
"os" "os"
"time" "time"
) )
@ -27,11 +34,16 @@ const (
fileHeaderSignature = 0x04034b50 fileHeaderSignature = 0x04034b50
directoryHeaderSignature = 0x02014b50 directoryHeaderSignature = 0x02014b50
directoryEndSignature = 0x06054b50 directoryEndSignature = 0x06054b50
directory64LocSignature = 0x07064b50
directory64EndSignature = 0x06064b50
dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder
fileHeaderLen = 30 // + filename + extra fileHeaderLen = 30 // + filename + extra
directoryHeaderLen = 46 // + filename + extra + comment directoryHeaderLen = 46 // + filename + extra + comment
directoryEndLen = 22 // + comment directoryEndLen = 22 // + comment
dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size dataDescriptorLen = 16 // four uint32: descriptor signature, crc32, compressed size, size
dataDescriptor64Len = 24 // descriptor with 8 byte sizes
directory64LocLen = 20 //
directory64EndLen = 56 // + extra
// Constants for the first byte in CreatorVersion // Constants for the first byte in CreatorVersion
creatorFAT = 0 creatorFAT = 0
@ -39,6 +51,17 @@ const (
creatorNTFS = 11 creatorNTFS = 11
creatorVFAT = 14 creatorVFAT = 14
creatorMacOSX = 19 creatorMacOSX = 19
// version numbers
zipVersion20 = 20 // 2.0
zipVersion45 = 45 // 4.5 (reads and writes zip64 archives)
// limits for non zip64 files
uint16max = (1 << 16) - 1
uint32max = (1 << 32) - 1
// extra header id's
zip64ExtraId = 0x0001 // zip64 Extended Information Extra Field
) )
type FileHeader struct { type FileHeader struct {
@ -50,8 +73,10 @@ type FileHeader struct {
ModifiedTime uint16 // MS-DOS time ModifiedTime uint16 // MS-DOS time
ModifiedDate uint16 // MS-DOS date ModifiedDate uint16 // MS-DOS date
CRC32 uint32 CRC32 uint32
CompressedSize uint32 CompressedSize uint32 // deprecated; use CompressedSize64
UncompressedSize uint32 UncompressedSize uint32 // deprecated; use UncompressedSize64
CompressedSize64 uint64
UncompressedSize64 uint64
Extra []byte Extra []byte
ExternalAttrs uint32 // Meaning depends on CreatorVersion ExternalAttrs uint32 // Meaning depends on CreatorVersion
Comment string Comment string
@ -68,7 +93,12 @@ type headerFileInfo struct {
} }
func (fi headerFileInfo) Name() string { return fi.fh.Name } func (fi headerFileInfo) Name() string { return fi.fh.Name }
func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) } func (fi headerFileInfo) Size() int64 {
if fi.fh.UncompressedSize64 > 0 {
return int64(fi.fh.UncompressedSize64)
}
return int64(fi.fh.UncompressedSize)
}
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() } func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() } func (fi headerFileInfo) ModTime() time.Time { return fi.fh.ModTime() }
func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() } func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
@ -78,25 +108,27 @@ func (fi headerFileInfo) Sys() interface{} { return fi.fh }
// os.FileInfo. // os.FileInfo.
func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
size := fi.Size() size := fi.Size()
if size > (1<<32 - 1) {
return nil, errors.New("zip: file over 4GB")
}
fh := &FileHeader{ fh := &FileHeader{
Name: fi.Name(), Name: fi.Name(),
UncompressedSize: uint32(size), UncompressedSize64: uint64(size),
} }
fh.SetModTime(fi.ModTime()) fh.SetModTime(fi.ModTime())
fh.SetMode(fi.Mode()) fh.SetMode(fi.Mode())
if fh.UncompressedSize64 > uint32max {
fh.UncompressedSize = uint32max
} else {
fh.UncompressedSize = uint32(fh.UncompressedSize64)
}
return fh, nil return fh, nil
} }
type directoryEnd struct { type directoryEnd struct {
diskNbr uint16 // unused diskNbr uint32 // unused
dirDiskNbr uint16 // unused dirDiskNbr uint32 // unused
dirRecordsThisDisk uint16 // unused dirRecordsThisDisk uint64 // unused
directoryRecords uint16 directoryRecords uint64
directorySize uint32 directorySize uint64
directoryOffset uint32 // relative to file directoryOffset uint64 // relative to file
commentLen uint16 commentLen uint16
comment string comment string
} }
@ -190,6 +222,11 @@ func (h *FileHeader) SetMode(mode os.FileMode) {
} }
} }
// isZip64 returns true if the file size exceeds the 32 bit limit
func (fh *FileHeader) isZip64() bool {
return fh.CompressedSize64 > uint32max || fh.UncompressedSize64 > uint32max
}
func msdosModeToFileMode(m uint32) (mode os.FileMode) { func msdosModeToFileMode(m uint32) (mode os.FileMode) {
if m&msdosDir != 0 { if m&msdosDir != 0 {
mode = os.ModeDir | 0777 mode = os.ModeDir | 0777

BIN
libgo/go/archive/zip/testdata/zip64.zip vendored Normal file

Binary file not shown.

View File

@ -27,7 +27,7 @@ type Writer struct {
type header struct { type header struct {
*FileHeader *FileHeader
offset uint32 offset uint64
} }
// NewWriter returns a new Writer writing a zip file to w. // NewWriter returns a new Writer writing a zip file to w.
@ -62,14 +62,36 @@ func (w *Writer) Close() error {
b.uint16(h.ModifiedTime) b.uint16(h.ModifiedTime)
b.uint16(h.ModifiedDate) b.uint16(h.ModifiedDate)
b.uint32(h.CRC32) b.uint32(h.CRC32)
if h.isZip64() || h.offset > uint32max {
// the file needs a zip64 header. store maxint in both
// 32 bit size fields (and offset later) to signal that the
// zip64 extra header should be used.
b.uint32(uint32max) // compressed size
b.uint32(uint32max) // uncompressed size
// append a zip64 extra block to Extra
var buf [28]byte // 2x uint16 + 3x uint64
eb := writeBuf(buf[:])
eb.uint16(zip64ExtraId)
eb.uint16(24) // size = 3x uint64
eb.uint64(h.UncompressedSize64)
eb.uint64(h.CompressedSize64)
eb.uint64(h.offset)
h.Extra = append(h.Extra, buf[:]...)
} else {
b.uint32(h.CompressedSize) b.uint32(h.CompressedSize)
b.uint32(h.UncompressedSize) b.uint32(h.UncompressedSize)
}
b.uint16(uint16(len(h.Name))) b.uint16(uint16(len(h.Name)))
b.uint16(uint16(len(h.Extra))) b.uint16(uint16(len(h.Extra)))
b.uint16(uint16(len(h.Comment))) b.uint16(uint16(len(h.Comment)))
b = b[4:] // skip disk number start and internal file attr (2x uint16) b = b[4:] // skip disk number start and internal file attr (2x uint16)
b.uint32(h.ExternalAttrs) b.uint32(h.ExternalAttrs)
b.uint32(h.offset) if h.offset > uint32max {
b.uint32(uint32max)
} else {
b.uint32(uint32(h.offset))
}
if _, err := w.cw.Write(buf[:]); err != nil { if _, err := w.cw.Write(buf[:]); err != nil {
return err return err
} }
@ -85,15 +107,52 @@ func (w *Writer) Close() error {
} }
end := w.cw.count end := w.cw.count
records := uint64(len(w.dir))
size := uint64(end - start)
offset := uint64(start)
if records > uint16max || size > uint32max || offset > uint32max {
var buf [directory64EndLen + directory64LocLen]byte
b := writeBuf(buf[:])
// zip64 end of central directory record
b.uint32(directory64EndSignature)
b.uint64(directory64EndLen)
b.uint16(zipVersion45) // version made by
b.uint16(zipVersion45) // version needed to extract
b.uint32(0) // number of this disk
b.uint32(0) // number of the disk with the start of the central directory
b.uint64(records) // total number of entries in the central directory on this disk
b.uint64(records) // total number of entries in the central directory
b.uint64(size) // size of the central directory
b.uint64(offset) // offset of start of central directory with respect to the starting disk number
// zip64 end of central directory locator
b.uint32(directory64LocSignature)
b.uint32(0) // number of the disk with the start of the zip64 end of central directory
b.uint64(uint64(end)) // relative offset of the zip64 end of central directory record
b.uint32(1) // total number of disks
if _, err := w.cw.Write(buf[:]); err != nil {
return err
}
// store max values in the regular end record to signal that
// that the zip64 values should be used instead
records = uint16max
size = uint32max
offset = uint32max
}
// write end record // write end record
var buf [directoryEndLen]byte var buf [directoryEndLen]byte
b := writeBuf(buf[:]) b := writeBuf(buf[:])
b.uint32(uint32(directoryEndSignature)) b.uint32(uint32(directoryEndSignature))
b = b[4:] // skip over disk number and first disk number (2x uint16) b = b[4:] // skip over disk number and first disk number (2x uint16)
b.uint16(uint16(len(w.dir))) // number of entries this disk b.uint16(uint16(records)) // number of entries this disk
b.uint16(uint16(len(w.dir))) // number of entries total b.uint16(uint16(records)) // number of entries total
b.uint32(uint32(end - start)) // size of directory b.uint32(uint32(size)) // size of directory
b.uint32(uint32(start)) // start of directory b.uint32(uint32(offset)) // start of directory
// skipped size of comment (always zero) // skipped size of comment (always zero)
if _, err := w.cw.Write(buf[:]); err != nil { if _, err := w.cw.Write(buf[:]); err != nil {
return err return err
@ -127,8 +186,9 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
} }
fh.Flags |= 0x8 // we will write a data descriptor fh.Flags |= 0x8 // we will write a data descriptor
fh.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14
fh.ReaderVersion = 0x14 fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
fh.ReaderVersion = zipVersion20
fw := &fileWriter{ fw := &fileWriter{
zipw: w.cw, zipw: w.cw,
@ -151,7 +211,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
h := &header{ h := &header{
FileHeader: fh, FileHeader: fh,
offset: uint32(w.cw.count), offset: uint64(w.cw.count),
} }
w.dir = append(w.dir, h) w.dir = append(w.dir, h)
fw.header = h fw.header = h
@ -173,9 +233,9 @@ func writeHeader(w io.Writer, h *FileHeader) error {
b.uint16(h.Method) b.uint16(h.Method)
b.uint16(h.ModifiedTime) b.uint16(h.ModifiedTime)
b.uint16(h.ModifiedDate) b.uint16(h.ModifiedDate)
b.uint32(h.CRC32) b.uint32(0) // since we are writing a data descriptor crc32,
b.uint32(h.CompressedSize) b.uint32(0) // compressed size,
b.uint32(h.UncompressedSize) b.uint32(0) // and uncompressed size should be zero
b.uint16(uint16(len(h.Name))) b.uint16(uint16(len(h.Name)))
b.uint16(uint16(len(h.Extra))) b.uint16(uint16(len(h.Extra)))
if _, err := w.Write(buf[:]); err != nil { if _, err := w.Write(buf[:]); err != nil {
@ -218,17 +278,40 @@ func (w *fileWriter) close() error {
// update FileHeader // update FileHeader
fh := w.header.FileHeader fh := w.header.FileHeader
fh.CRC32 = w.crc32.Sum32() fh.CRC32 = w.crc32.Sum32()
fh.CompressedSize = uint32(w.compCount.count) fh.CompressedSize64 = uint64(w.compCount.count)
fh.UncompressedSize = uint32(w.rawCount.count) fh.UncompressedSize64 = uint64(w.rawCount.count)
// write data descriptor if fh.isZip64() {
var buf [dataDescriptorLen]byte fh.CompressedSize = uint32max
b := writeBuf(buf[:]) fh.UncompressedSize = uint32max
fh.ReaderVersion = zipVersion45 // requires 4.5 - File uses ZIP64 format extensions
} else {
fh.CompressedSize = uint32(fh.CompressedSize64)
fh.UncompressedSize = uint32(fh.UncompressedSize64)
}
// Write data descriptor. This is more complicated than one would
// think, see e.g. comments in zipfile.c:putextended() and
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7073588.
// The approach here is to write 8 byte sizes if needed without
// adding a zip64 extra in the local header (too late anyway).
var buf []byte
if fh.isZip64() {
buf = make([]byte, dataDescriptor64Len)
} else {
buf = make([]byte, dataDescriptorLen)
}
b := writeBuf(buf)
b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X b.uint32(dataDescriptorSignature) // de-facto standard, required by OS X
b.uint32(fh.CRC32) b.uint32(fh.CRC32)
if fh.isZip64() {
b.uint64(fh.CompressedSize64)
b.uint64(fh.UncompressedSize64)
} else {
b.uint32(fh.CompressedSize) b.uint32(fh.CompressedSize)
b.uint32(fh.UncompressedSize) b.uint32(fh.UncompressedSize)
_, err := w.zipw.Write(buf[:]) }
_, err := w.zipw.Write(buf)
return err return err
} }
@ -262,3 +345,8 @@ func (b *writeBuf) uint32(v uint32) {
binary.LittleEndian.PutUint32(*b, v) binary.LittleEndian.PutUint32(*b, v)
*b = (*b)[4:] *b = (*b)[4:]
} }
func (b *writeBuf) uint64(v uint64) {
binary.LittleEndian.PutUint64(*b, v)
*b = (*b)[8:]
}

View File

@ -9,7 +9,8 @@ package zip
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"reflect" "io"
"io/ioutil"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -58,6 +59,33 @@ func TestModTime(t *testing.T) {
} }
} }
func testHeaderRoundTrip(fh *FileHeader, wantUncompressedSize uint32, wantUncompressedSize64 uint64, t *testing.T) {
fi := fh.FileInfo()
fh2, err := FileInfoHeader(fi)
if err != nil {
t.Fatal(err)
}
if got, want := fh2.Name, fh.Name; got != want {
t.Errorf("Name: got %s, want %s\n", got, want)
}
if got, want := fh2.UncompressedSize, wantUncompressedSize; got != want {
t.Errorf("UncompressedSize: got %d, want %d\n", got, want)
}
if got, want := fh2.UncompressedSize64, wantUncompressedSize64; got != want {
t.Errorf("UncompressedSize64: got %d, want %d\n", got, want)
}
if got, want := fh2.ModifiedTime, fh.ModifiedTime; got != want {
t.Errorf("ModifiedTime: got %d, want %d\n", got, want)
}
if got, want := fh2.ModifiedDate, fh.ModifiedDate; got != want {
t.Errorf("ModifiedDate: got %d, want %d\n", got, want)
}
if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh {
t.Errorf("Sys didn't return original *FileHeader")
}
}
func TestFileHeaderRoundTrip(t *testing.T) { func TestFileHeaderRoundTrip(t *testing.T) {
fh := &FileHeader{ fh := &FileHeader{
Name: "foo.txt", Name: "foo.txt",
@ -65,17 +93,83 @@ func TestFileHeaderRoundTrip(t *testing.T) {
ModifiedTime: 1234, ModifiedTime: 1234,
ModifiedDate: 5678, ModifiedDate: 5678,
} }
fi := fh.FileInfo() testHeaderRoundTrip(fh, fh.UncompressedSize, uint64(fh.UncompressedSize), t)
fh2, err := FileInfoHeader(fi) }
// Ignore these fields: func TestFileHeaderRoundTrip64(t *testing.T) {
fh2.CreatorVersion = 0 fh := &FileHeader{
fh2.ExternalAttrs = 0 Name: "foo.txt",
UncompressedSize64: 9876543210,
if !reflect.DeepEqual(fh, fh2) { ModifiedTime: 1234,
t.Errorf("mismatch\n input=%#v\noutput=%#v\nerr=%v", fh, fh2, err) ModifiedDate: 5678,
} }
if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh { testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t)
t.Errorf("Sys didn't return original *FileHeader") }
func TestZip64(t *testing.T) {
if testing.Short() {
t.Logf("slow test; skipping")
return
}
// write 2^32 bytes plus "END\n" to a zip file
buf := new(bytes.Buffer)
w := NewWriter(buf)
f, err := w.Create("huge.txt")
if err != nil {
t.Fatal(err)
}
chunk := make([]byte, 1024)
for i := range chunk {
chunk[i] = '.'
}
chunk[len(chunk)-1] = '\n'
end := []byte("END\n")
for i := 0; i < (1<<32)/1024; i++ {
_, err := f.Write(chunk)
if err != nil {
t.Fatal("write chunk:", err)
}
}
_, err = f.Write(end)
if err != nil {
t.Fatal("write end:", err)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}
// read back zip file and check that we get to the end of it
r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
if err != nil {
t.Fatal("reader:", err)
}
f0 := r.File[0]
rc, err := f0.Open()
if err != nil {
t.Fatal("opening:", err)
}
for i := 0; i < (1<<32)/1024; i++ {
_, err := io.ReadFull(rc, chunk)
if err != nil {
t.Fatal("read:", err)
}
}
gotEnd, err := ioutil.ReadAll(rc)
if err != nil {
t.Fatal("read end:", err)
}
if !bytes.Equal(gotEnd, end) {
t.Errorf("End of zip64 archive %q, want %q", gotEnd, end)
}
err = rc.Close()
if err != nil {
t.Fatal("closing:", err)
}
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 {
t.Errorf("UncompressedSize64 %d, want %d", got, want)
} }
} }

View File

@ -375,6 +375,41 @@ func (b *Reader) ReadString(delim byte) (line string, err error) {
return string(bytes), e return string(bytes), e
} }
// WriteTo implements io.WriterTo.
func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
n, err = b.writeBuf(w)
if err != nil {
return
}
if r, ok := b.rd.(io.WriterTo); ok {
m, err := r.WriteTo(w)
n += m
return n, err
}
for b.fill(); b.r < b.w; b.fill() {
m, err := b.writeBuf(w)
n += m
if err != nil {
return n, err
}
}
if b.err == io.EOF {
b.err = nil
}
return n, b.readErr()
}
// writeBuf writes the Reader's buffer to the writer.
func (b *Reader) writeBuf(w io.Writer) (int64, error) {
n, err := w.Write(b.buf[b.r:b.w])
b.r += n
return int64(n), err
}
// buffered output // buffered output
// Writer implements buffering for an io.Writer object. // Writer implements buffering for an io.Writer object.

View File

@ -762,3 +762,107 @@ func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) {
} }
} }
} }
func TestReaderWriteTo(t *testing.T) {
input := make([]byte, 8192)
for i := range input {
// 101 and 251 are arbitrary prime numbers.
// The idea is to create an input sequence
// which doesn't repeat too frequently.
input[i] = byte(i % 251)
if i%101 == 0 {
input[i] ^= byte(i / 101)
}
}
r := NewReader(bytes.NewBuffer(input))
w := new(bytes.Buffer)
if n, err := r.WriteTo(w); err != nil || n != int64(len(input)) {
t.Fatalf("r.WriteTo(w) = %d, %v, want %d, nil", n, err, len(input))
}
for i, val := range w.Bytes() {
if val != input[i] {
t.Errorf("after write: out[%d] = %#x, want %#x", i, val, input[i])
}
}
}
type errorWriterToTest struct {
rn, wn int
rerr, werr error
expected error
}
func (r errorWriterToTest) Read(p []byte) (int, error) {
return len(p) * r.rn, r.rerr
}
func (w errorWriterToTest) Write(p []byte) (int, error) {
return len(p) * w.wn, w.werr
}
var errorWriterToTests = []errorWriterToTest{
{1, 0, nil, io.ErrClosedPipe, io.ErrClosedPipe},
{0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe},
{0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrClosedPipe},
{0, 1, io.EOF, nil, nil},
}
func TestReaderWriteToErrors(t *testing.T) {
for i, rw := range errorWriterToTests {
r := NewReader(rw)
if _, err := r.WriteTo(rw); err != rw.expected {
t.Errorf("r.WriteTo(errorWriterToTests[%d]) = _, %v, want _,%v", i, err, rw.expected)
}
}
}
// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
type onlyReader struct {
r io.Reader
}
func (r *onlyReader) Read(b []byte) (int, error) {
return r.r.Read(b)
}
// An onlyWriter only implements io.Writer, no matter what other methods the underlying implementation may have.
type onlyWriter struct {
w io.Writer
}
func (w *onlyWriter) Write(b []byte) (int, error) {
return w.w.Write(b)
}
func BenchmarkReaderCopyOptimal(b *testing.B) {
// Optimal case is where the underlying reader implements io.WriterTo
for i := 0; i < b.N; i++ {
b.StopTimer()
src := NewReader(bytes.NewBuffer(make([]byte, 8192)))
dst := &onlyWriter{new(bytes.Buffer)}
b.StartTimer()
io.Copy(dst, src)
}
}
func BenchmarkReaderCopyUnoptimal(b *testing.B) {
// Unoptimal case is where the underlying reader doesn't implement io.WriterTo
for i := 0; i < b.N; i++ {
b.StopTimer()
src := NewReader(&onlyReader{bytes.NewBuffer(make([]byte, 8192))})
dst := &onlyWriter{new(bytes.Buffer)}
b.StartTimer()
io.Copy(dst, src)
}
}
func BenchmarkReaderCopyNoWriteTo(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
src := &onlyReader{NewReader(bytes.NewBuffer(make([]byte, 8192)))}
dst := &onlyWriter{new(bytes.Buffer)}
b.StartTimer()
io.Copy(dst, src)
}
}

View File

@ -99,6 +99,19 @@ func (b *Buffer) grow(n int) int {
return b.off + m return b.off + m
} }
// Grow grows the buffer's capacity, if necessary, to guarantee space for
// another n bytes. After Grow(n), at least n bytes can be written to the
// buffer without another allocation.
// If n is negative, Grow will panic.
// If the buffer can't grow it will panic with ErrTooLarge.
func (b *Buffer) Grow(n int) {
if n < 0 {
panic("bytes.Buffer.Grow: negative count")
}
m := b.grow(n)
b.buf = b.buf[0:m]
}
// Write appends the contents of p to the buffer. The return // Write appends the contents of p to the buffer. The return
// value n is the length of p; err is always nil. // value n is the length of p; err is always nil.
// If the buffer becomes too large, Write will panic with // If the buffer becomes too large, Write will panic with

View File

@ -8,20 +8,21 @@ import (
. "bytes" . "bytes"
"io" "io"
"math/rand" "math/rand"
"runtime"
"testing" "testing"
"unicode/utf8" "unicode/utf8"
) )
const N = 10000 // make this bigger for a larger (and slower) test const N = 10000 // make this bigger for a larger (and slower) test
var data string // test data for write tests var data string // test data for write tests
var bytes []byte // test data; same as data but as a slice. var testBytes []byte // test data; same as data but as a slice.
func init() { func init() {
bytes = make([]byte, N) testBytes = make([]byte, N)
for i := 0; i < N; i++ { for i := 0; i < N; i++ {
bytes[i] = 'a' + byte(i%26) testBytes[i] = 'a' + byte(i%26)
} }
data = string(bytes) data = string(testBytes)
} }
// Verify that contents of buf match the string s. // Verify that contents of buf match the string s.
@ -84,7 +85,7 @@ func fillBytes(t *testing.T, testname string, buf *Buffer, s string, n int, fub
} }
func TestNewBuffer(t *testing.T) { func TestNewBuffer(t *testing.T) {
buf := NewBuffer(bytes) buf := NewBuffer(testBytes)
check(t, "NewBuffer", buf, data) check(t, "NewBuffer", buf, data)
} }
@ -187,7 +188,7 @@ func TestLargeByteWrites(t *testing.T) {
limit = 9 limit = 9
} }
for i := 3; i < limit; i += 3 { for i := 3; i < limit; i += 3 {
s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, bytes) s := fillBytes(t, "TestLargeWrites (1)", &buf, "", 5, testBytes)
empty(t, "TestLargeByteWrites (2)", &buf, s, make([]byte, len(data)/i)) empty(t, "TestLargeByteWrites (2)", &buf, s, make([]byte, len(data)/i))
} }
check(t, "TestLargeByteWrites (3)", &buf, "") check(t, "TestLargeByteWrites (3)", &buf, "")
@ -205,7 +206,7 @@ func TestLargeStringReads(t *testing.T) {
func TestLargeByteReads(t *testing.T) { func TestLargeByteReads(t *testing.T) {
var buf Buffer var buf Buffer
for i := 3; i < 30; i += 3 { for i := 3; i < 30; i += 3 {
s := fillBytes(t, "TestLargeReads (1)", &buf, "", 5, bytes[0:len(bytes)/i]) s := fillBytes(t, "TestLargeReads (1)", &buf, "", 5, testBytes[0:len(testBytes)/i])
empty(t, "TestLargeReads (2)", &buf, s, make([]byte, len(data))) empty(t, "TestLargeReads (2)", &buf, s, make([]byte, len(data)))
} }
check(t, "TestLargeByteReads (3)", &buf, "") check(t, "TestLargeByteReads (3)", &buf, "")
@ -219,7 +220,7 @@ func TestMixedReadsAndWrites(t *testing.T) {
if i%2 == 0 { if i%2 == 0 {
s = fillString(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, data[0:wlen]) s = fillString(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, data[0:wlen])
} else { } else {
s = fillBytes(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, bytes[0:wlen]) s = fillBytes(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, testBytes[0:wlen])
} }
rlen := rand.Intn(len(data)) rlen := rand.Intn(len(data))
@ -240,7 +241,7 @@ func TestNil(t *testing.T) {
func TestReadFrom(t *testing.T) { func TestReadFrom(t *testing.T) {
var buf Buffer var buf Buffer
for i := 3; i < 30; i += 3 { for i := 3; i < 30; i += 3 {
s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, bytes[0:len(bytes)/i]) s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, testBytes[0:len(testBytes)/i])
var b Buffer var b Buffer
b.ReadFrom(&buf) b.ReadFrom(&buf)
empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data))) empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data)))
@ -250,7 +251,7 @@ func TestReadFrom(t *testing.T) {
func TestWriteTo(t *testing.T) { func TestWriteTo(t *testing.T) {
var buf Buffer var buf Buffer
for i := 3; i < 30; i += 3 { for i := 3; i < 30; i += 3 {
s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, bytes[0:len(bytes)/i]) s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, testBytes[0:len(testBytes)/i])
var b Buffer var b Buffer
buf.WriteTo(&b) buf.WriteTo(&b)
empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data))) empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data)))
@ -374,6 +375,37 @@ func TestReadBytes(t *testing.T) {
} }
} }
func TestGrow(t *testing.T) {
x := []byte{'x'}
y := []byte{'y'}
tmp := make([]byte, 72)
for _, startLen := range []int{0, 100, 1000, 10000, 100000} {
xBytes := Repeat(x, startLen)
for _, growLen := range []int{0, 100, 1000, 10000, 100000} {
buf := NewBuffer(xBytes)
// If we read, this affects buf.off, which is good to test.
readBytes, _ := buf.Read(tmp)
buf.Grow(growLen)
yBytes := Repeat(y, growLen)
// Check no allocation occurs in write, as long as we're single-threaded.
var m1, m2 runtime.MemStats
runtime.ReadMemStats(&m1)
buf.Write(yBytes)
runtime.ReadMemStats(&m2)
if runtime.GOMAXPROCS(-1) == 1 && m1.Mallocs != m2.Mallocs {
t.Errorf("allocation occurred during write")
}
// Check that buffer has correct data.
if !Equal(buf.Bytes()[0:startLen-readBytes], xBytes[readBytes:]) {
t.Errorf("bad initial data at %d %d", startLen, growLen)
}
if !Equal(buf.Bytes()[startLen-readBytes:startLen-readBytes+growLen], yBytes) {
t.Errorf("bad written data at %d %d", startLen, growLen)
}
}
}
}
// Was a bug: used to give EOF reading empty slice at EOF. // Was a bug: used to give EOF reading empty slice at EOF.
func TestReadEmptyAtEOF(t *testing.T) { func TestReadEmptyAtEOF(t *testing.T) {
b := new(Buffer) b := new(Buffer)

View File

@ -333,14 +333,15 @@ func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
return a[0:na] return a[0:na]
} }
// Join concatenates the elements of a to create a single byte array. The separator // Join concatenates the elements of a to create a new byte array. The separator
// sep is placed between elements in the resulting array. // sep is placed between elements in the resulting array.
func Join(a [][]byte, sep []byte) []byte { func Join(a [][]byte, sep []byte) []byte {
if len(a) == 0 { if len(a) == 0 {
return []byte{} return []byte{}
} }
if len(a) == 1 { if len(a) == 1 {
return a[0] // Just return a copy.
return append([]byte(nil), a[0]...)
} }
n := len(sep) * (len(a) - 1) n := len(sep) * (len(a) - 1)
for i := 0; i < len(a); i++ { for i := 0; i < len(a); i++ {
@ -619,10 +620,8 @@ func Replace(s, old, new []byte, n int) []byte {
m = Count(s, old) m = Count(s, old)
} }
if m == 0 { if m == 0 {
// Nothing to do. Just copy. // Just return a copy.
t := make([]byte, len(s)) return append([]byte(nil), s...)
copy(t, s)
return t
} }
if n < 0 || m < n { if n < 0 || m < n {
n = m n = m

View File

@ -6,6 +6,7 @@ package bytes_test
import ( import (
. "bytes" . "bytes"
"math/rand"
"reflect" "reflect"
"testing" "testing"
"unicode" "unicode"
@ -490,6 +491,12 @@ func TestSplit(t *testing.T) {
t.Errorf("Split disagrees withSplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a) t.Errorf("Split disagrees withSplitN(%q, %q, %d) = %v; want %v", tt.s, tt.sep, tt.n, b, a)
} }
} }
if len(a) > 0 {
in, out := a[0], s
if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] {
t.Errorf("Join(%#v, %q) didn't copy", a, tt.sep)
}
}
} }
} }
@ -561,6 +568,14 @@ func TestFields(t *testing.T) {
} }
func TestFieldsFunc(t *testing.T) { func TestFieldsFunc(t *testing.T) {
for _, tt := range fieldstests {
a := FieldsFunc([]byte(tt.s), unicode.IsSpace)
result := arrayOfString(a)
if !eq(result, tt.a) {
t.Errorf("FieldsFunc(%q, unicode.IsSpace) = %v; want %v", tt.s, a, tt.a)
continue
}
}
pred := func(c rune) bool { return c == 'X' } pred := func(c rune) bool { return c == 'X' }
var fieldsFuncTests = []FieldsTest{ var fieldsFuncTests = []FieldsTest{
{"", []string{}}, {"", []string{}},
@ -1008,3 +1023,39 @@ func TestEqualFold(t *testing.T) {
} }
} }
} }
var makeFieldsInput = func() []byte {
x := make([]byte, 1<<20)
// Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.
for i := range x {
switch rand.Intn(10) {
case 0:
x[i] = ' '
case 1:
if i > 0 && x[i-1] == 'x' {
copy(x[i-1:], "χ")
break
}
fallthrough
default:
x[i] = 'x'
}
}
return x
}
var fieldsInput = makeFieldsInput()
func BenchmarkFields(b *testing.B) {
b.SetBytes(int64(len(fieldsInput)))
for i := 0; i < b.N; i++ {
Fields(fieldsInput)
}
}
func BenchmarkFieldsFunc(b *testing.B) {
b.SetBytes(int64(len(fieldsInput)))
for i := 0; i < b.N; i++ {
FieldsFunc(fieldsInput, unicode.IsSpace)
}
}

View File

@ -5,23 +5,24 @@
package bytes_test package bytes_test
import ( import (
. "bytes" "bytes"
"encoding/base64" "encoding/base64"
"fmt"
"io" "io"
"os" "os"
) )
func ExampleBuffer() { func ExampleBuffer() {
var b Buffer // A Buffer needs no initialization. var b bytes.Buffer // A Buffer needs no initialization.
b.Write([]byte("Hello ")) b.Write([]byte("Hello "))
b.Write([]byte("world!")) fmt.Fprintf(&b, "world!")
b.WriteTo(os.Stdout) b.WriteTo(os.Stdout)
// Output: Hello world! // Output: Hello world!
} }
func ExampleBuffer_reader() { func ExampleBuffer_reader() {
// A Buffer can turn a string or a []byte into an io.Reader. // A Buffer can turn a string or a []byte into an io.Reader.
buf := NewBufferString("R29waGVycyBydWxlIQ==") buf := bytes.NewBufferString("R29waGVycyBydWxlIQ==")
dec := base64.NewDecoder(base64.StdEncoding, buf) dec := base64.NewDecoder(base64.StdEncoding, buf)
io.Copy(os.Stdout, dec) io.Copy(os.Stdout, dec)
// Output: Gophers rule! // Output: Gophers rule!

View File

@ -0,0 +1,17 @@
// Copyright 2012 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 flate
// forwardCopy is like the built-in copy function except that it always goes
// forward from the start, even if the dst and src overlap.
func forwardCopy(dst, src []byte) int {
if len(src) > len(dst) {
src = src[:len(dst)]
}
for i, x := range src {
dst[i] = x
}
return len(src)
}

View File

@ -0,0 +1,52 @@
// Copyright 2012 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 flate
import (
"testing"
)
func TestForwardCopy(t *testing.T) {
testCases := []struct {
dst0, dst1 int
src0, src1 int
want string
}{
{0, 9, 0, 9, "012345678"},
{0, 5, 4, 9, "45678"},
{4, 9, 0, 5, "01230"},
{1, 6, 3, 8, "34567"},
{3, 8, 1, 6, "12121"},
{0, 9, 3, 6, "345"},
{3, 6, 0, 9, "012"},
{1, 6, 0, 9, "00000"},
{0, 4, 7, 8, "7"},
{0, 1, 6, 8, "6"},
{4, 4, 6, 9, ""},
{2, 8, 6, 6, ""},
{0, 0, 0, 0, ""},
}
for _, tc := range testCases {
b := []byte("0123456789")
dst := b[tc.dst0:tc.dst1]
src := b[tc.src0:tc.src1]
n := forwardCopy(dst, src)
got := string(dst[:n])
if got != tc.want {
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)
}
// Check that the bytes outside of dst[:n] were not modified.
for i, x := range b {
if i >= tc.dst0 && i < tc.dst0+n {
continue
}
if int(x) != '0'+i {
t.Errorf("dst=b[%d:%d], src=b[%d:%d]: copy overrun at b[%d]: got '%c', want '%c'",
tc.dst0, tc.dst1, tc.src0, tc.src1, i, x, '0'+i)
}
}
}
}

View File

@ -334,7 +334,7 @@ var deflateInflateStringTests = []deflateInflateStringTest{
{ {
"../testdata/e.txt", "../testdata/e.txt",
"2.718281828...", "2.718281828...",
[...]int{10013, 5065, 5096, 5115, 5093, 5079, 5079, 5079, 5079, 5079}, [...]int{100018, 50650, 50960, 51150, 50930, 50790, 50790, 50790, 50790, 50790},
}, },
{ {
"../testdata/Mark.Twain-Tom.Sawyer.txt", "../testdata/Mark.Twain-Tom.Sawyer.txt",

View File

@ -212,7 +212,7 @@ type decompressor struct {
codebits [numCodes]int codebits [numCodes]int
// Output history, buffer. // Output history, buffer.
hist [maxHist]byte hist *[maxHist]byte
hp int // current output position in buffer hp int // current output position in buffer
hw int // have written hist[0:hw] already hw int // have written hist[0:hw] already
hfull bool // buffer has filled at least once hfull bool // buffer has filled at least once
@ -511,51 +511,49 @@ func (f *decompressor) huffmanBlock() {
return return
} }
p := f.hp - dist f.copyLen, f.copyDist = length, dist
if p < 0 { if f.copyHist() {
p += len(f.hist)
}
for i := 0; i < length; i++ {
f.hist[f.hp] = f.hist[p]
f.hp++
p++
if f.hp == len(f.hist) {
// After flush continue copying out of history.
f.copyLen = length - (i + 1)
f.copyDist = dist
f.flush((*decompressor).copyHuff)
return return
} }
if p == len(f.hist) {
p = 0
}
}
} }
panic("unreached") panic("unreached")
} }
func (f *decompressor) copyHuff() { // copyHist copies f.copyLen bytes from f.hist (f.copyDist bytes ago) to itself.
length := f.copyLen // It reports whether the f.hist buffer is full.
dist := f.copyDist func (f *decompressor) copyHist() bool {
p := f.hp - dist p := f.hp - f.copyDist
if p < 0 { if p < 0 {
p += len(f.hist) p += len(f.hist)
} }
for i := 0; i < length; i++ { for f.copyLen > 0 {
f.hist[f.hp] = f.hist[p] n := f.copyLen
f.hp++ if x := len(f.hist) - f.hp; n > x {
p++ n = x
}
if x := len(f.hist) - p; n > x {
n = x
}
forwardCopy(f.hist[f.hp:f.hp+n], f.hist[p:p+n])
p += n
f.hp += n
f.copyLen -= n
if f.hp == len(f.hist) { if f.hp == len(f.hist) {
f.copyLen = length - (i + 1) // After flush continue copying out of history.
f.flush((*decompressor).copyHuff) f.flush((*decompressor).copyHuff)
return return true
} }
if p == len(f.hist) { if p == len(f.hist) {
p = 0 p = 0
} }
} }
return false
}
// Continue processing Huffman block. func (f *decompressor) copyHuff() {
if f.copyHist() {
return
}
f.huffmanBlock() f.huffmanBlock()
} }
@ -590,9 +588,9 @@ func (f *decompressor) dataBlock() {
f.copyData() f.copyData()
} }
// copyData copies f.copyLen bytes from the underlying reader into f.hist.
// It pauses for reads when f.hist is full.
func (f *decompressor) copyData() { func (f *decompressor) copyData() {
// Read f.dataLen bytes into history,
// pausing for reads as history fills.
n := f.copyLen n := f.copyLen
for n > 0 { for n > 0 {
m := len(f.hist) - f.hp m := len(f.hist) - f.hp
@ -695,6 +693,7 @@ func makeReader(r io.Reader) Reader {
func NewReader(r io.Reader) io.ReadCloser { func NewReader(r io.Reader) io.ReadCloser {
var f decompressor var f decompressor
f.r = makeReader(r) f.r = makeReader(r)
f.hist = new([maxHist]byte)
f.step = (*decompressor).nextBlock f.step = (*decompressor).nextBlock
return &f return &f
} }
@ -706,8 +705,9 @@ func NewReader(r io.Reader) io.ReadCloser {
// to read data compressed by NewWriterDict. // to read data compressed by NewWriterDict.
func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser { func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser {
var f decompressor var f decompressor
f.setDict(dict)
f.r = makeReader(r) f.r = makeReader(r)
f.hist = new([maxHist]byte)
f.step = (*decompressor).nextBlock f.step = (*decompressor).nextBlock
f.setDict(dict)
return &f return &f
} }

View File

@ -0,0 +1,60 @@
// Copyright 2012 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 flate
import (
"io/ioutil"
"runtime"
"testing"
)
func benchmarkEncoder(b *testing.B, testfile, level, n int) {
b.StopTimer()
b.SetBytes(int64(n))
buf0, err := ioutil.ReadFile(testfiles[testfile])
if err != nil {
b.Fatal(err)
}
if len(buf0) == 0 {
b.Fatalf("test file %q has no data", testfiles[testfile])
}
buf1 := make([]byte, n)
for i := 0; i < n; i += len(buf0) {
if len(buf0) > n-i {
buf0 = buf0[:n-i]
}
copy(buf1[i:], buf0)
}
buf0 = nil
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
w, err := NewWriter(ioutil.Discard, level)
if err != nil {
b.Fatal(err)
}
w.Write(buf1)
w.Close()
}
}
func BenchmarkEncodeDigitsSpeed1e4(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e4) }
func BenchmarkEncodeDigitsSpeed1e5(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e5) }
func BenchmarkEncodeDigitsSpeed1e6(b *testing.B) { benchmarkEncoder(b, digits, speed, 1e6) }
func BenchmarkEncodeDigitsDefault1e4(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e4) }
func BenchmarkEncodeDigitsDefault1e5(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e5) }
func BenchmarkEncodeDigitsDefault1e6(b *testing.B) { benchmarkEncoder(b, digits, default_, 1e6) }
func BenchmarkEncodeDigitsCompress1e4(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e4) }
func BenchmarkEncodeDigitsCompress1e5(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e5) }
func BenchmarkEncodeDigitsCompress1e6(b *testing.B) { benchmarkEncoder(b, digits, compress, 1e6) }
func BenchmarkEncodeTwainSpeed1e4(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e4) }
func BenchmarkEncodeTwainSpeed1e5(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e5) }
func BenchmarkEncodeTwainSpeed1e6(b *testing.B) { benchmarkEncoder(b, twain, speed, 1e6) }
func BenchmarkEncodeTwainDefault1e4(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e4) }
func BenchmarkEncodeTwainDefault1e5(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e5) }
func BenchmarkEncodeTwainDefault1e6(b *testing.B) { benchmarkEncoder(b, twain, default_, 1e6) }
func BenchmarkEncodeTwainCompress1e4(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e4) }
func BenchmarkEncodeTwainCompress1e5(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e5) }
func BenchmarkEncodeTwainCompress1e6(b *testing.B) { benchmarkEncoder(b, twain, compress, 1e6) }

View File

@ -114,11 +114,19 @@ func TestReader(t *testing.T) {
func benchmarkDecoder(b *testing.B, n int) { func benchmarkDecoder(b *testing.B, n int) {
b.StopTimer() b.StopTimer()
b.SetBytes(int64(n)) b.SetBytes(int64(n))
buf0, _ := ioutil.ReadFile("../testdata/e.txt") buf0, err := ioutil.ReadFile("../testdata/e.txt")
buf0 = buf0[:10000] if err != nil {
b.Fatal(err)
}
if len(buf0) == 0 {
b.Fatalf("test file has no data")
}
compressed := new(bytes.Buffer) compressed := new(bytes.Buffer)
w := NewWriter(compressed, LSB, 8) w := NewWriter(compressed, LSB, 8)
for i := 0; i < n; i += len(buf0) { for i := 0; i < n; i += len(buf0) {
if len(buf0) > n-i {
buf0 = buf0[:n-i]
}
io.Copy(w, bytes.NewBuffer(buf0)) io.Copy(w, bytes.NewBuffer(buf0))
} }
w.Close() w.Close()

View File

@ -131,13 +131,14 @@ func (e *encoder) incHi() error {
} }
// Write writes a compressed representation of p to e's underlying writer. // Write writes a compressed representation of p to e's underlying writer.
func (e *encoder) Write(p []byte) (int, error) { func (e *encoder) Write(p []byte) (n int, err error) {
if e.err != nil { if e.err != nil {
return 0, e.err return 0, e.err
} }
if len(p) == 0 { if len(p) == 0 {
return 0, nil return 0, nil
} }
n = len(p)
litMask := uint32(1<<e.litWidth - 1) litMask := uint32(1<<e.litWidth - 1)
code := e.savedCode code := e.savedCode
if code == invalidCode { if code == invalidCode {
@ -167,11 +168,11 @@ loop:
code = literal code = literal
// Increment e.hi, the next implied code. If we run out of codes, reset // Increment e.hi, the next implied code. If we run out of codes, reset
// the encoder state (including clearing the hash table) and continue. // the encoder state (including clearing the hash table) and continue.
if err := e.incHi(); err != nil { if err1 := e.incHi(); err1 != nil {
if err == errOutOfCodes { if err1 == errOutOfCodes {
continue continue
} }
e.err = err e.err = err1
return 0, e.err return 0, e.err
} }
// Otherwise, insert key -> e.hi into the map that e.table represents. // Otherwise, insert key -> e.hi into the map that e.table represents.
@ -184,7 +185,7 @@ loop:
} }
} }
e.savedCode = code e.savedCode = code
return len(p), nil return n, nil
} }
// Close closes the encoder, flushing any pending output. It does not close or // Close closes the encoder, flushing any pending output. It does not close or

View File

@ -96,13 +96,29 @@ func TestWriter(t *testing.T) {
} }
} }
func TestWriterReturnValues(t *testing.T) {
w := NewWriter(ioutil.Discard, LSB, 8)
n, err := w.Write([]byte("asdf"))
if n != 4 || err != nil {
t.Errorf("got %d, %v, want 4, nil", n, err)
}
}
func benchmarkEncoder(b *testing.B, n int) { func benchmarkEncoder(b *testing.B, n int) {
b.StopTimer() b.StopTimer()
b.SetBytes(int64(n)) b.SetBytes(int64(n))
buf0, _ := ioutil.ReadFile("../testdata/e.txt") buf0, err := ioutil.ReadFile("../testdata/e.txt")
buf0 = buf0[:10000] if err != nil {
b.Fatal(err)
}
if len(buf0) == 0 {
b.Fatalf("test file has no data")
}
buf1 := make([]byte, n) buf1 := make([]byte, n)
for i := 0; i < n; i += len(buf0) { for i := 0; i < n; i += len(buf0) {
if len(buf0) > n-i {
buf0 = buf0[:n-i]
}
copy(buf1[i:], buf0) copy(buf1[i:], buf0)
} }
buf0 = nil buf0 = nil

View File

@ -1,4 +1,4 @@
The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete The Project Gutenberg EBook of The Adventures of Tom Sawyer, Complete
by Mark Twain (Samuel Clemens) by Mark Twain (Samuel Clemens)
This eBook is for the use of anyone anywhere at no cost and with This eBook is for the use of anyone anywhere at no cost and with

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@ and compress during writing. For example, to write compressed data
to a buffer: to a buffer:
var b bytes.Buffer var b bytes.Buffer
w, err := zlib.NewWriter(&b) w := zlib.NewWriter(&b)
w.Write([]byte("hello, world\n")) w.Write([]byte("hello, world\n"))
w.Close() w.Close()

View File

@ -79,7 +79,7 @@ func Remove(h Interface, i int) interface{} {
func up(h Interface, j int) { func up(h Interface, j int) {
for { for {
i := (j - 1) / 2 // parent i := (j - 1) / 2 // parent
if i == j || h.Less(i, j) { if i == j || !h.Less(j, i) {
break break
} }
h.Swap(i, j) h.Swap(i, j)
@ -97,7 +97,7 @@ func down(h Interface, i, n int) {
if j2 := j1 + 1; j2 < n && !h.Less(j1, j2) { if j2 := j1 + 1; j2 < n && !h.Less(j1, j2) {
j = j2 // = 2*i + 2 // right child j = j2 // = 2*i + 2 // right child
} }
if h.Less(i, j) { if !h.Less(j, i) {
break break
} }
h.Swap(i, j) h.Swap(i, j)

View File

@ -170,3 +170,16 @@ func TestRemove2(t *testing.T) {
} }
} }
} }
func BenchmarkDup(b *testing.B) {
const n = 10000
h := make(myHeap, n)
for i := 0; i < b.N; i++ {
for j := 0; j < n; j++ {
Push(&h, 0) // all elements are the same
}
for h.Len() > 0 {
Pop(&h)
}
}
}

View File

@ -11,201 +11,185 @@
// //
package list package list
// Element is an element in the linked list. // Element is an element of a linked list.
type Element struct { type Element struct {
// Next and previous pointers in the doubly-linked list of elements. // Next and previous pointers in the doubly-linked list of elements.
// The front of the list has prev = nil, and the back has next = nil. // To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *Element next, prev *Element
// The list to which this element belongs. // The list to which this element belongs.
list *List list *List
// The contents of this list element. // The value stored with this element.
Value interface{} Value interface{}
} }
// Next returns the next list element or nil. // Next returns the next list element or nil.
func (e *Element) Next() *Element { return e.next } func (e *Element) Next() *Element {
if p := e.next; p != &e.list.root {
return p
}
return nil
}
// Prev returns the previous list element or nil. // Prev returns the previous list element or nil.
func (e *Element) Prev() *Element { return e.prev } func (e *Element) Prev() *Element {
if p := e.prev; p != &e.list.root {
return p
}
return nil
}
// List represents a doubly linked list. // List represents a doubly linked list.
// The zero value for List is an empty list ready to use. // The zero value for List is an empty list ready to use.
type List struct { type List struct {
front, back *Element root Element // sentinel list element, only &root, root.prev, and root.next are used
len int len int // current list length excluding (this) sentinel element
} }
// Init initializes or clears a List. // Init initializes or clears list l.
func (l *List) Init() *List { func (l *List) Init() *List {
l.front = nil l.root.next = &l.root
l.back = nil l.root.prev = &l.root
l.len = 0 l.len = 0
return l return l
} }
// New returns an initialized list. // New returns an initialized list.
func New() *List { return new(List) } func New() *List { return new(List).Init() }
// Front returns the first element in the list. // Len returns the number of elements of list l.
func (l *List) Front() *Element { return l.front } func (l *List) Len() int { return l.len }
// Back returns the last element in the list. // Front returns the first element of list l or nil
func (l *List) Back() *Element { return l.back } func (l *List) Front() *Element {
if l.len == 0 {
return nil
}
return l.root.next
}
// Remove removes the element from the list // Back returns the last element of list l or nil.
// and returns its Value. func (l *List) Back() *Element {
if l.len == 0 {
return nil
}
return l.root.prev
}
// lazyInit lazily initializes a zero List value.
func (l *List) lazyInit() {
if l.root.next == nil {
l.Init()
}
}
// insert inserts e after at, increments l.len, and returns e.
func (l *List) insert(e, at *Element) *Element {
n := at.next
at.next = e
e.prev = at
e.next = n
n.prev = e
e.list = l
l.len++
return e
}
// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
func (l *List) insertValue(v interface{}, at *Element) *Element {
return l.insert(&Element{Value: v}, at)
}
// remove removes e from its list, decrements l.len, and returns e.
func (l *List) remove(e *Element) *Element {
e.prev.next = e.next
e.next.prev = e.prev
e.list = nil
l.len--
return e
}
// Remove removes e from l if e is an element of list l.
// It returns the element value e.Value.
func (l *List) Remove(e *Element) interface{} { func (l *List) Remove(e *Element) interface{} {
if e.list == l {
// if e.list == l, l must have been initialized when e was inserted
// in l or l == nil (e is a zero Element) and l.remove will crash
l.remove(e) l.remove(e)
e.list = nil // do what remove does not }
return e.Value return e.Value
} }
// remove the element from the list, but do not clear the Element's list field. // Pushfront inserts a new element e with value v at the front of list l and returns e.
// This is so that other List methods may use remove when relocating Elements func (l *List) PushFront(v interface{}) *Element {
// without needing to restore the list field. l.lazyInit()
func (l *List) remove(e *Element) { return l.insertValue(v, &l.root)
if e.list != l {
return
}
if e.prev == nil {
l.front = e.next
} else {
e.prev.next = e.next
}
if e.next == nil {
l.back = e.prev
} else {
e.next.prev = e.prev
}
e.prev = nil
e.next = nil
l.len--
} }
func (l *List) insertBefore(e *Element, mark *Element) { // PushBack inserts a new element e with value v at the back of list l and returns e.
if mark.prev == nil { func (l *List) PushBack(v interface{}) *Element {
// new front of the list l.lazyInit()
l.front = e return l.insertValue(v, l.root.prev)
} else {
mark.prev.next = e
}
e.prev = mark.prev
mark.prev = e
e.next = mark
l.len++
} }
func (l *List) insertAfter(e *Element, mark *Element) { // InsertBefore inserts a new element e with value v immediately before mark and returns e.
if mark.next == nil { // If mark is not an element of l, the list is not modified.
// new back of the list func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
l.back = e
} else {
mark.next.prev = e
}
e.next = mark.next
mark.next = e
e.prev = mark
l.len++
}
func (l *List) insertFront(e *Element) {
if l.front == nil {
// empty list
l.front, l.back = e, e
e.prev, e.next = nil, nil
l.len = 1
return
}
l.insertBefore(e, l.front)
}
func (l *List) insertBack(e *Element) {
if l.back == nil {
// empty list
l.front, l.back = e, e
e.prev, e.next = nil, nil
l.len = 1
return
}
l.insertAfter(e, l.back)
}
// PushFront inserts the value at the front of the list and returns a new Element containing the value.
func (l *List) PushFront(value interface{}) *Element {
e := &Element{nil, nil, l, value}
l.insertFront(e)
return e
}
// PushBack inserts the value at the back of the list and returns a new Element containing the value.
func (l *List) PushBack(value interface{}) *Element {
e := &Element{nil, nil, l, value}
l.insertBack(e)
return e
}
// InsertBefore inserts the value immediately before mark and returns a new Element containing the value.
func (l *List) InsertBefore(value interface{}, mark *Element) *Element {
if mark.list != l { if mark.list != l {
return nil return nil
} }
e := &Element{nil, nil, l, value} // see comment in List.Remove about initialization of l
l.insertBefore(e, mark) return l.insertValue(v, mark.prev)
return e
} }
// InsertAfter inserts the value immediately after mark and returns a new Element containing the value. // InsertAfter inserts a new element e with value v immediately after mark and returns e.
func (l *List) InsertAfter(value interface{}, mark *Element) *Element { // If mark is not an element of l, the list is not modified.
func (l *List) InsertAfter(v interface{}, mark *Element) *Element {
if mark.list != l { if mark.list != l {
return nil return nil
} }
e := &Element{nil, nil, l, value} // see comment in List.Remove about initialization of l
l.insertAfter(e, mark) return l.insertValue(v, mark)
return e
} }
// MoveToFront moves the element to the front of the list. // MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
func (l *List) MoveToFront(e *Element) { func (l *List) MoveToFront(e *Element) {
if e.list != l || l.front == e { if e.list != l || l.root.next == e {
return return
} }
l.remove(e) // see comment in List.Remove about initialization of l
l.insertFront(e) l.insert(l.remove(e), &l.root)
} }
// MoveToBack moves the element to the back of the list. // MoveToBack moves element e to the back of list l.
// If e is not an element of l, the list is not modified.
func (l *List) MoveToBack(e *Element) { func (l *List) MoveToBack(e *Element) {
if e.list != l || l.back == e { if e.list != l || l.root.prev == e {
return return
} }
l.remove(e) // see comment in List.Remove about initialization of l
l.insertBack(e) l.insert(l.remove(e), l.root.prev)
} }
// Len returns the number of elements in the list. // PuchBackList inserts a copy of an other list at the back of list l.
func (l *List) Len() int { return l.len } // The lists l and other may be the same.
func (l *List) PushBackList(other *List) {
// PushBackList inserts each element of ol at the back of the list. l.lazyInit()
func (l *List) PushBackList(ol *List) { for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
last := ol.Back() l.insertValue(e.Value, l.root.prev)
for e := ol.Front(); e != nil; e = e.Next() {
l.PushBack(e.Value)
if e == last {
break
}
} }
} }
// PushFrontList inserts each element of ol at the front of the list. The ordering of the passed list is preserved. // PushFrontList inserts a copy of an other list at the front of list l.
func (l *List) PushFrontList(ol *List) { // The lists l and other may be the same.
first := ol.Front() func (l *List) PushFrontList(other *List) {
for e := ol.Back(); e != nil; e = e.Prev() { l.lazyInit()
l.PushFront(e.Value) for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
if e == first { l.insertValue(e.Value, &l.root)
break
}
} }
} }

View File

@ -4,65 +4,75 @@
package list package list
import ( import "testing"
"testing"
) func checkListLen(t *testing.T, l *List, len int) bool {
if n := l.Len(); n != len {
t.Errorf("l.Len() = %d, want %d", n, len)
return false
}
return true
}
func checkListPointers(t *testing.T, l *List, es []*Element) { func checkListPointers(t *testing.T, l *List, es []*Element) {
if len(es) == 0 { root := &l.root
if l.front != nil || l.back != nil {
t.Errorf("l.front/l.back = %v/%v should be nil/nil", l.front, l.back) if !checkListLen(t, l, len(es)) {
}
return return
} }
if l.front != es[0] { // zero length lists must be the zero value or properly initialized (sentinel circle)
t.Errorf("l.front = %v, want %v", l.front, es[0]) if len(es) == 0 {
if l.root.next != nil && l.root.next != root || l.root.prev != nil && l.root.prev != root {
t.Errorf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p", l.root.next, l.root.prev, root)
} }
if last := es[len(es)-1]; l.back != last { return
t.Errorf("l.back = %v, want %v", l.back, last)
} }
// len(es) > 0
// check internal and external prev/next connections
for i, e := range es { for i, e := range es {
var e_prev, e_next *Element = nil, nil prev := root
Prev := (*Element)(nil)
if i > 0 { if i > 0 {
e_prev = es[i-1] prev = es[i-1]
Prev = prev
} }
if i < len(es)-1 { if p := e.prev; p != prev {
e_next = es[i+1] t.Errorf("elt[%d](%p).prev = %p, want %p", i, e, p, prev)
} }
if e.prev != e_prev { if p := e.Prev(); p != Prev {
t.Errorf("elt #%d (%v) has prev=%v, want %v", i, e, e.prev, e_prev) t.Errorf("elt[%d](%p).Prev() = %p, want %p", i, e, p, Prev)
} }
if e.next != e_next {
t.Errorf("elt #%d (%v) has next=%v, want %v", i, e, e.next, e_next)
}
}
}
func checkListLen(t *testing.T, l *List, n int) { next := root
if an := l.Len(); an != n { Next := (*Element)(nil)
t.Errorf("l.Len() = %d, want %d", an, n) if i < len(es)-1 {
next = es[i+1]
Next = next
}
if n := e.next; n != next {
t.Errorf("elt[%d](%p).next = %p, want %p", i, e, n, next)
}
if n := e.Next(); n != Next {
t.Errorf("elt[%d](%p).Next() = %p, want %p", i, e, n, Next)
}
} }
} }
func TestList(t *testing.T) { func TestList(t *testing.T) {
l := New() l := New()
checkListPointers(t, l, []*Element{}) checkListPointers(t, l, []*Element{})
checkListLen(t, l, 0)
// Single element list // Single element list
e := l.PushFront("a") e := l.PushFront("a")
checkListLen(t, l, 1)
checkListPointers(t, l, []*Element{e}) checkListPointers(t, l, []*Element{e})
l.MoveToFront(e) l.MoveToFront(e)
checkListPointers(t, l, []*Element{e}) checkListPointers(t, l, []*Element{e})
l.MoveToBack(e) l.MoveToBack(e)
checkListPointers(t, l, []*Element{e}) checkListPointers(t, l, []*Element{e})
checkListLen(t, l, 1)
l.Remove(e) l.Remove(e)
checkListPointers(t, l, []*Element{}) checkListPointers(t, l, []*Element{})
checkListLen(t, l, 0)
// Bigger list // Bigger list
e2 := l.PushFront(2) e2 := l.PushFront(2)
@ -70,11 +80,9 @@ func TestList(t *testing.T) {
e3 := l.PushBack(3) e3 := l.PushBack(3)
e4 := l.PushBack("banana") e4 := l.PushBack("banana")
checkListPointers(t, l, []*Element{e1, e2, e3, e4}) checkListPointers(t, l, []*Element{e1, e2, e3, e4})
checkListLen(t, l, 4)
l.Remove(e2) l.Remove(e2)
checkListPointers(t, l, []*Element{e1, e3, e4}) checkListPointers(t, l, []*Element{e1, e3, e4})
checkListLen(t, l, 3)
l.MoveToFront(e3) // move from middle l.MoveToFront(e3) // move from middle
checkListPointers(t, l, []*Element{e3, e1, e4}) checkListPointers(t, l, []*Element{e3, e1, e4})
@ -121,7 +129,7 @@ func TestList(t *testing.T) {
} }
} }
if sum != 4 { if sum != 4 {
t.Errorf("sum over l.Iter() = %d, want 4", sum) t.Errorf("sum over l = %d, want 4", sum)
} }
// Clear all elements by iterating // Clear all elements by iterating
@ -131,19 +139,18 @@ func TestList(t *testing.T) {
l.Remove(e) l.Remove(e)
} }
checkListPointers(t, l, []*Element{}) checkListPointers(t, l, []*Element{})
checkListLen(t, l, 0)
} }
func checkList(t *testing.T, l *List, es []interface{}) { func checkList(t *testing.T, l *List, es []interface{}) {
if l.Len() != len(es) { if !checkListLen(t, l, len(es)) {
t.Errorf("list has len=%v, want %v", l.Len(), len(es))
return return
} }
i := 0 i := 0
for e := l.Front(); e != nil; e = e.Next() { for e := l.Front(); e != nil; e = e.Next() {
le := e.Value.(int) le := e.Value.(int)
if le != es[i] { if le != es[i] {
t.Errorf("elt #%d has value=%v, want %v", i, le, es[i]) t.Errorf("elt[%d].Value = %v, want %v", i, le, es[i])
} }
i++ i++
} }
@ -202,8 +209,27 @@ func TestRemove(t *testing.T) {
e := l.Front() e := l.Front()
l.Remove(e) l.Remove(e)
checkListPointers(t, l, []*Element{e2}) checkListPointers(t, l, []*Element{e2})
checkListLen(t, l, 1)
l.Remove(e) l.Remove(e)
checkListPointers(t, l, []*Element{e2}) checkListPointers(t, l, []*Element{e2})
checkListLen(t, l, 1) }
func TestIssue4103(t *testing.T) {
l1 := New()
l1.PushBack(1)
l1.PushBack(2)
l2 := New()
l2.PushBack(3)
l2.PushBack(4)
e := l1.Front()
l2.Remove(e) // l2 should not change because e is not an element of l2
if n := l2.Len(); n != 2 {
t.Errorf("l2.Len() = %d, want 2", n)
}
l1.InsertBefore(8, e)
if n := l1.Len(); n != 3 {
t.Errorf("l1.Len() = %d, want 3", n)
}
} }

View File

@ -221,7 +221,10 @@ L:
if tt.dec != nil { if tt.dec != nil {
dec = make([]uint32, len(tt.dec)) dec = make([]uint32, len(tt.dec))
} }
expandKey(tt.key, enc, dec) // This test could only test Go version of expandKey because asm
// version might use different memory layout for expanded keys
// This is OK because we don't expose expanded keys to the outside
expandKeyGo(tt.key, enc, dec)
for j, v := range enc { for j, v := range enc {
if v != tt.enc[j] { if v != tt.enc[j] {
t.Errorf("key %d: enc[%d] = %#x, want %#x", i, j, v, tt.enc[j]) t.Errorf("key %d: enc[%d] = %#x, want %#x", i, j, v, tt.enc[j])
@ -352,15 +355,39 @@ func TestCipherDecrypt(t *testing.T) {
} }
func BenchmarkEncrypt(b *testing.B) { func BenchmarkEncrypt(b *testing.B) {
b.StopTimer()
tt := encryptTests[0] tt := encryptTests[0]
c, err := NewCipher(tt.key) c, err := NewCipher(tt.key)
if err != nil { if err != nil {
b.Fatal("NewCipher:", err) b.Fatal("NewCipher:", err)
} }
out := make([]byte, len(tt.in)) out := make([]byte, len(tt.in))
b.StartTimer() b.SetBytes(int64(len(out)))
b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
c.Encrypt(out, tt.in) c.Encrypt(out, tt.in)
} }
} }
func BenchmarkDecrypt(b *testing.B) {
tt := encryptTests[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)
}
}
func BenchmarkExpand(b *testing.B) {
tt := encryptTests[0]
n := len(tt.key) + 28
c := &aesCipher{make([]uint32, n), make([]uint32, n)}
b.ResetTimer()
for i := 0; i < b.N; i++ {
expandKey(tt.key, c.enc, c.dec)
}
}

View File

@ -37,7 +37,7 @@
package aes package aes
// Encrypt one block from src into dst, using the expanded key xk. // Encrypt one block from src into dst, using the expanded key xk.
func encryptBlock(xk []uint32, dst, src []byte) { func encryptBlockGo(xk []uint32, dst, src []byte) {
var s0, s1, s2, s3, t0, t1, t2, t3 uint32 var s0, s1, s2, s3, t0, t1, t2, t3 uint32
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
@ -82,7 +82,7 @@ func encryptBlock(xk []uint32, dst, src []byte) {
} }
// Decrypt one block from src into dst, using the expanded key xk. // Decrypt one block from src into dst, using the expanded key xk.
func decryptBlock(xk []uint32, dst, src []byte) { func decryptBlockGo(xk []uint32, dst, src []byte) {
var s0, s1, s2, s3, t0, t1, t2, t3 uint32 var s0, s1, s2, s3, t0, t1, t2, t3 uint32
s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3]) s0 = uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
@ -139,7 +139,7 @@ func rotw(w uint32) uint32 { return w<<8 | w>>24 }
// Key expansion algorithm. See FIPS-197, Figure 11. // Key expansion algorithm. See FIPS-197, Figure 11.
// Their rcon[i] is our powx[i-1] << 24. // Their rcon[i] is our powx[i-1] << 24.
func expandKey(key []byte, enc, dec []uint32) { func expandKeyGo(key []byte, enc, dec []uint32) {
// Encryption key setup. // Encryption key setup.
var i int var i int
nk := len(key) / 4 nk := len(key) / 4

View File

@ -45,6 +45,10 @@ func NewCipher(key []byte) (cipher.Block, error) {
func (c *aesCipher) BlockSize() int { return BlockSize } func (c *aesCipher) BlockSize() int { return BlockSize }
func (c *aesCipher) Encrypt(dst, src []byte) { encryptBlock(c.enc, dst, src) } func (c *aesCipher) Encrypt(dst, src []byte) {
encryptBlock(c.enc, dst, src)
}
func (c *aesCipher) Decrypt(dst, src []byte) { decryptBlock(c.dec, dst, src) } func (c *aesCipher) Decrypt(dst, src []byte) {
decryptBlock(c.dec, dst, src)
}

View File

@ -0,0 +1,46 @@
// Copyright 2012 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.
// +build amd64
package aes
// defined in asm_$GOARCH.s
func hasAsm() bool
func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
var useAsm = hasAsm()
func encryptBlock(xk []uint32, dst, src []byte) {
if useAsm {
encryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0])
} else {
encryptBlockGo(xk, dst, src)
}
}
func decryptBlock(xk []uint32, dst, src []byte) {
if useAsm {
decryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0])
} else {
decryptBlockGo(xk, dst, src)
}
}
func expandKey(key []byte, enc, dec []uint32) {
if useAsm {
rounds := 10
switch len(key) {
case 128 / 8:
rounds = 10
case 192 / 8:
rounds = 12
case 256 / 8:
rounds = 14
}
expandKeyAsm(rounds, &key[0], &enc[0], &dec[0])
} else {
expandKeyGo(key, enc, dec)
}
}

View File

@ -0,0 +1,19 @@
// Copyright 2012 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.
// +build !amd64
package aes
func encryptBlock(xk []uint32, dst, src []byte) {
encryptBlockGo(xk, dst, src)
}
func decryptBlock(xk []uint32, dst, src []byte) {
decryptBlockGo(xk, dst, src)
}
func expandKey(key []byte, enc, dec []uint32) {
expandKeyGo(key, enc, dec)
}

View File

@ -140,14 +140,16 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
w := new(big.Int).ModInverse(s, N) w := new(big.Int).ModInverse(s, N)
u1 := e.Mul(e, w) u1 := e.Mul(e, w)
u1.Mod(u1, N)
u2 := w.Mul(r, w) u2 := w.Mul(r, w)
u2.Mod(u2, N)
x1, y1 := c.ScalarBaseMult(u1.Bytes()) x1, y1 := c.ScalarBaseMult(u1.Bytes())
x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes()) x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
if x1.Cmp(x2) == 0 { x, y := c.Add(x1, y1, x2, y2)
if x.Sign() == 0 && y.Sign() == 0 {
return false return false
} }
x, _ := c.Add(x1, y1, x2, y2)
x.Mod(x, N) x.Mod(x, N)
return x.Cmp(r) == 0 return x.Cmp(r) == 0
} }

View File

@ -5,11 +5,19 @@
package ecdsa package ecdsa
import ( import (
"bufio"
"compress/bzip2"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/sha1" "crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex" "encoding/hex"
"hash"
"io"
"math/big" "math/big"
"os"
"strings"
"testing" "testing"
) )
@ -72,156 +80,112 @@ func fromHex(s string) *big.Int {
return r return r
} }
// These test vectors were taken from
// http://csrc.nist.gov/groups/STM/cavp/documents/dss/ecdsatestvectors.zip
var testVectors = []struct {
msg string
Qx, Qy string
r, s string
ok bool
}{
{
"09626b45493672e48f3d1226a3aff3201960e577d33a7f72c7eb055302db8fe8ed61685dd036b554942a5737cd1512cdf811ee0c00e6dd2f08c69f08643be396e85dafda664801e772cdb7396868ac47b172245b41986aa2648cb77fbbfa562581be06651355a0c4b090f9d17d8f0ab6cced4e0c9d386cf465a516630f0231bd",
"9504b5b82d97a264d8b3735e0568decabc4b6ca275bc53cbadfc1c40",
"03426f80e477603b10dee670939623e3da91a94267fc4e51726009ed",
"81d3ac609f9575d742028dd496450a58a60eea2dcf8b9842994916e1",
"96a8c5f382c992e8f30ccce9af120b067ec1d74678fa8445232f75a5",
false,
},
{
"96b2b6536f6df29be8567a72528aceeaccbaa66c66c534f3868ca9778b02faadb182e4ed34662e73b9d52ecbe9dc8e875fc05033c493108b380689ebf47e5b062e6a0cdb3dd34ce5fe347d92768d72f7b9b377c20aea927043b509c078ed2467d7113405d2ddd458811e6faf41c403a2a239240180f1430a6f4330df5d77de37",
"851e3100368a22478a0029353045ae40d1d8202ef4d6533cfdddafd8",
"205302ac69457dd345e86465afa72ee8c74ca97e2b0b999aec1f10c2",
"4450c2d38b697e990721aa2dbb56578d32b4f5aeb3b9072baa955ee0",
"e26d4b589166f7b4ba4b1c8fce823fa47aad22f8c9c396b8c6526e12",
false,
},
{
"86778dbb4a068a01047a8d245d632f636c11d2ad350740b36fad90428b454ad0f120cb558d12ea5c8a23db595d87543d06d1ef489263d01ee529871eb68737efdb8ff85bc7787b61514bed85b7e01d6be209e0a4eb0db5c8df58a5c5bf706d76cb2bdf7800208639e05b89517155d11688236e6a47ed37d8e5a2b1e0adea338e",
"ad5bda09d319a717c1721acd6688d17020b31b47eef1edea57ceeffc",
"c8ce98e181770a7c9418c73c63d01494b8b80a41098c5ea50692c984",
"de5558c257ab4134e52c19d8db3b224a1899cbd08cc508ce8721d5e9",
"745db7af5a477e5046705c0a5eff1f52cb94a79d481f0c5a5e108ecd",
true,
},
{
"4bc6ef1958556686dab1e39c3700054a304cbd8f5928603dcd97fafd1f29e69394679b638f71c9344ce6a535d104803d22119f57b5f9477e253817a52afa9bfbc9811d6cc8c8be6b6566c6ef48b439bbb532abe30627548c598867f3861ba0b154dc1c3deca06eb28df8efd28258554b5179883a36fbb1eecf4f93ee19d41e3d",
"cc5eea2edf964018bdc0504a3793e4d2145142caa09a72ac5fb8d3e8",
"a48d78ae5d08aa725342773975a00d4219cf7a8029bb8cf3c17c374a",
"67b861344b4e416d4094472faf4272f6d54a497177fbc5f9ef292836",
"1d54f3fcdad795bf3b23408ecbac3e1321d1d66f2e4e3d05f41f7020",
false,
},
{
"bb658732acbf3147729959eb7318a2058308b2739ec58907dd5b11cfa3ecf69a1752b7b7d806fe00ec402d18f96039f0b78dbb90a59c4414fb33f1f4e02e4089de4122cd93df5263a95be4d7084e2126493892816e6a5b4ed123cb705bf930c8f67af0fb4514d5769232a9b008a803af225160ce63f675bd4872c4c97b146e5e",
"6234c936e27bf141fc7534bfc0a7eedc657f91308203f1dcbd642855",
"27983d87ca785ef4892c3591ef4a944b1deb125dd58bd351034a6f84",
"e94e05b42d01d0b965ffdd6c3a97a36a771e8ea71003de76c4ecb13f",
"1dc6464ffeefbd7872a081a5926e9fc3e66d123f1784340ba17737e9",
false,
},
{
"7c00be9123bfa2c4290be1d8bc2942c7f897d9a5b7917e3aabd97ef1aab890f148400a89abd554d19bec9d8ed911ce57b22fbcf6d30ca2115f13ce0a3f569a23bad39ee645f624c49c60dcfc11e7d2be24de9c905596d8f23624d63dc46591d1f740e46f982bfae453f107e80db23545782be23ce43708245896fc54e1ee5c43",
"9f3f037282aaf14d4772edffff331bbdda845c3f65780498cde334f1",
"8308ee5a16e3bcb721b6bc30000a0419bc1aaedd761be7f658334066",
"6381d7804a8808e3c17901e4d283b89449096a8fba993388fa11dc54",
"8e858f6b5b253686a86b757bad23658cda53115ac565abca4e3d9f57",
false,
},
{
"cffc122a44840dc705bb37130069921be313d8bde0b66201aebc48add028ca131914ef2e705d6bedd19dc6cf9459bbb0f27cdfe3c50483808ffcdaffbeaa5f062e097180f07a40ef4ab6ed03fe07ed6bcfb8afeb42c97eafa2e8a8df469de07317c5e1494c41547478eff4d8c7d9f0f484ad90fedf6e1c35ee68fa73f1691601",
"a03b88a10d930002c7b17ca6af2fd3e88fa000edf787dc594f8d4fd4",
"e0cf7acd6ddc758e64847fe4df9915ebda2f67cdd5ec979aa57421f5",
"387b84dcf37dc343c7d2c5beb82f0bf8bd894b395a7b894565d296c1",
"4adc12ce7d20a89ce3925e10491c731b15ddb3f339610857a21b53b4",
false,
},
{
"26e0e0cafd85b43d16255908ccfd1f061c680df75aba3081246b337495783052ba06c60f4a486c1591a4048bae11b4d7fec4f161d80bdc9a7b79d23e44433ed625eab280521a37f23dd3e1bdc5c6a6cfaa026f3c45cf703e76dab57add93fe844dd4cda67dc3bddd01f9152579e49df60969b10f09ce9372fdd806b0c7301866",
"9a8983c42f2b5a87c37a00458b5970320d247f0c8a88536440173f7d",
"15e489ec6355351361900299088cfe8359f04fe0cab78dde952be80c",
"929a21baa173d438ec9f28d6a585a2f9abcfc0a4300898668e476dc0",
"59a853f046da8318de77ff43f26fe95a92ee296fa3f7e56ce086c872",
true,
},
{
"1078eac124f48ae4f807e946971d0de3db3748dd349b14cca5c942560fb25401b2252744f18ad5e455d2d97ed5ae745f55ff509c6c8e64606afe17809affa855c4c4cdcaf6b69ab4846aa5624ed0687541aee6f2224d929685736c6a23906d974d3c257abce1a3fb8db5951b89ecb0cda92b5207d93f6618fd0f893c32cf6a6e",
"d6e55820bb62c2be97650302d59d667a411956138306bd566e5c3c2b",
"631ab0d64eaf28a71b9cbd27a7a88682a2167cee6251c44e3810894f",
"65af72bc7721eb71c2298a0eb4eed3cec96a737cc49125706308b129",
"bd5a987c78e2d51598dbd9c34a9035b0069c580edefdacee17ad892a",
false,
},
{
"919deb1fdd831c23481dfdb2475dcbe325b04c34f82561ced3d2df0b3d749b36e255c4928973769d46de8b95f162b53cd666cad9ae145e7fcfba97919f703d864efc11eac5f260a5d920d780c52899e5d76f8fe66936ff82130761231f536e6a3d59792f784902c469aa897aabf9a0678f93446610d56d5e0981e4c8a563556b",
"269b455b1024eb92d860a420f143ac1286b8cce43031562ae7664574",
"baeb6ca274a77c44a0247e5eb12ca72bdd9a698b3f3ae69c9f1aaa57",
"cb4ec2160f04613eb0dfe4608486091a25eb12aa4dec1afe91cfb008",
"40b01d8cd06589481574f958b98ca08ade9d2a8fe31024375c01bb40",
false,
},
{
"6e012361250dacf6166d2dd1aa7be544c3206a9d43464b3fcd90f3f8cf48d08ec099b59ba6fe7d9bdcfaf244120aed1695d8be32d1b1cd6f143982ab945d635fb48a7c76831c0460851a3d62b7209c30cd9c2abdbe3d2a5282a9fcde1a6f418dd23c409bc351896b9b34d7d3a1a63bbaf3d677e612d4a80fa14829386a64b33f",
"6d2d695efc6b43b13c14111f2109608f1020e3e03b5e21cfdbc82fcd",
"26a4859296b7e360b69cf40be7bd97ceaffa3d07743c8489fc47ca1b",
"9a8cb5f2fdc288b7183c5b32d8e546fc2ed1ca4285eeae00c8b572ad",
"8c623f357b5d0057b10cdb1a1593dab57cda7bdec9cf868157a79b97",
true,
},
{
"bf6bd7356a52b234fe24d25557200971fc803836f6fec3cade9642b13a8e7af10ab48b749de76aada9d8927f9b12f75a2c383ca7358e2566c4bb4f156fce1fd4e87ef8c8d2b6b1bdd351460feb22cdca0437ac10ca5e0abbbce9834483af20e4835386f8b1c96daaa41554ceee56730aac04f23a5c765812efa746051f396566",
"14250131b2599939cf2d6bc491be80ddfe7ad9de644387ee67de2d40",
"b5dc473b5d014cd504022043c475d3f93c319a8bdcb7262d9e741803",
"4f21642f2201278a95339a80f75cc91f8321fcb3c9462562f6cbf145",
"452a5f816ea1f75dee4fd514fa91a0d6a43622981966c59a1b371ff8",
false,
},
{
"0eb7f4032f90f0bd3cf9473d6d9525d264d14c031a10acd31a053443ed5fe919d5ac35e0be77813071b4062f0b5fdf58ad5f637b76b0b305aec18f82441b6e607b44cdf6e0e3c7c57f24e6fd565e39430af4a6b1d979821ed0175fa03e3125506847654d7e1ae904ce1190ae38dc5919e257bdac2db142a6e7cd4da6c2e83770",
"d1f342b7790a1667370a1840255ac5bbbdc66f0bc00ae977d99260ac",
"76416cabae2de9a1000b4646338b774baabfa3db4673790771220cdb",
"bc85e3fc143d19a7271b2f9e1c04b86146073f3fab4dda1c3b1f35ca",
"9a5c70ede3c48d5f43307a0c2a4871934424a3303b815df4bb0f128e",
false,
},
{
"5cc25348a05d85e56d4b03cec450128727bc537c66ec3a9fb613c151033b5e86878632249cba83adcefc6c1e35dcd31702929c3b57871cda5c18d1cf8f9650a25b917efaed56032e43b6fc398509f0d2997306d8f26675f3a8683b79ce17128e006aa0903b39eeb2f1001be65de0520115e6f919de902b32c38d691a69c58c92",
"7e49a7abf16a792e4c7bbc4d251820a2abd22d9f2fc252a7bf59c9a6",
"44236a8fb4791c228c26637c28ae59503a2f450d4cfb0dc42aa843b9",
"084461b4050285a1a85b2113be76a17878d849e6bc489f4d84f15cd8",
"079b5bddcc4d45de8dbdfd39f69817c7e5afa454a894d03ee1eaaac3",
false,
},
{
"1951533ce33afb58935e39e363d8497a8dd0442018fd96dff167b3b23d7206a3ee182a3194765df4768a3284e23b8696c199b4686e670d60c9d782f08794a4bccc05cffffbd1a12acd9eb1cfa01f7ebe124da66ecff4599ea7720c3be4bb7285daa1a86ebf53b042bd23208d468c1b3aa87381f8e1ad63e2b4c2ba5efcf05845",
"31945d12ebaf4d81f02be2b1768ed80784bf35cf5e2ff53438c11493",
"a62bebffac987e3b9d3ec451eb64c462cdf7b4aa0b1bbb131ceaa0a4",
"bc3c32b19e42b710bca5c6aaa128564da3ddb2726b25f33603d2af3c",
"ed1a719cc0c507edc5239d76fe50e2306c145ad252bd481da04180c0",
false,
},
}
func TestVectors(t *testing.T) { func TestVectors(t *testing.T) {
sha := sha1.New() // This test runs the full set of NIST test vectors from
// http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-3ecdsatestvectors.zip
//
// The SigVer.rsp file has been edited to remove test vectors for
// unsupported algorithms and has been compressed.
for i, test := range testVectors {
pub := PublicKey{
Curve: elliptic.P224(),
X: fromHex(test.Qx),
Y: fromHex(test.Qy),
}
msg, _ := hex.DecodeString(test.msg)
sha.Reset()
sha.Write(msg)
hashed := sha.Sum(nil)
r := fromHex(test.r)
s := fromHex(test.s)
if Verify(&pub, hashed, r, s) != test.ok {
t.Errorf("%d: bad result", i)
}
if testing.Short() { if testing.Short() {
return
}
f, err := os.Open("testdata/SigVer.rsp.bz2")
if err != nil {
t.Fatal(err)
}
buf := bufio.NewReader(bzip2.NewReader(f))
lineNo := 1
var h hash.Hash
var msg []byte
var hashed []byte
var r, s *big.Int
pub := new(PublicKey)
for {
line, err := buf.ReadString('\n')
if len(line) == 0 {
if err == io.EOF {
break break
} }
t.Fatalf("error reading from input: %s", err)
}
lineNo++
// Need to remove \r\n from the end of the line.
if !strings.HasSuffix(line, "\r\n") {
t.Fatalf("bad line ending (expected \\r\\n) on line %d", lineNo)
}
line = line[:len(line)-2]
if len(line) == 0 || line[0] == '#' {
continue
}
if line[0] == '[' {
line = line[1 : len(line)-1]
parts := strings.SplitN(line, ",", 2)
switch parts[0] {
case "P-224":
pub.Curve = elliptic.P224()
case "P-256":
pub.Curve = elliptic.P256()
case "P-384":
pub.Curve = elliptic.P384()
case "P-521":
pub.Curve = elliptic.P521()
default:
pub.Curve = nil
}
switch parts[1] {
case "SHA-1":
h = sha1.New()
case "SHA-224":
h = sha256.New224()
case "SHA-256":
h = sha256.New()
case "SHA-384":
h = sha512.New384()
case "SHA-512":
h = sha512.New()
default:
h = nil
}
continue
}
if h == nil || pub.Curve == nil {
continue
}
switch {
case strings.HasPrefix(line, "Msg = "):
if msg, err = hex.DecodeString(line[6:]); err != nil {
t.Fatalf("failed to decode message on line %d: %s", lineNo, err)
}
case strings.HasPrefix(line, "Qx = "):
pub.X = fromHex(line[5:])
case strings.HasPrefix(line, "Qy = "):
pub.Y = fromHex(line[5:])
case strings.HasPrefix(line, "R = "):
r = fromHex(line[4:])
case strings.HasPrefix(line, "S = "):
s = fromHex(line[4:])
case strings.HasPrefix(line, "Result = "):
expected := line[9] == 'P'
h.Reset()
h.Write(msg)
hashed := h.Sum(hashed[:0])
if Verify(pub, hashed, r, s) != expected {
t.Fatalf("incorrect result on line %d", lineNo)
}
default:
t.Fatalf("unknown variable on line %d: %s", lineNo, line)
}
} }
} }

View File

@ -31,10 +31,10 @@ type Curve interface {
// Double returns 2*(x,y) // Double returns 2*(x,y)
Double(x1, y1 *big.Int) (x, y *big.Int) Double(x1, y1 *big.Int) (x, y *big.Int)
// ScalarMult returns k*(Bx,By) where k is a number in big-endian form. // ScalarMult returns k*(Bx,By) where k is a number in big-endian form.
ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) ScalarMult(x1, y1 *big.Int, k []byte) (x, y *big.Int)
// ScalarBaseMult returns k*G, where G is the base point of the group and k // ScalarBaseMult returns k*G, where G is the base point of the group
// is an integer in big-endian form. // and k is an integer in big-endian form.
ScalarBaseMult(scalar []byte) (x, y *big.Int) ScalarBaseMult(k []byte) (x, y *big.Int)
} }
// CurveParams contains the parameters of an elliptic curve and also provides // CurveParams contains the parameters of an elliptic curve and also provides
@ -69,9 +69,24 @@ func (curve *CurveParams) IsOnCurve(x, y *big.Int) bool {
return x3.Cmp(y2) == 0 return x3.Cmp(y2) == 0
} }
// zForAffine returns a Jacobian Z value for the affine point (x, y). If x and
// y are zero, it assumes that they represent the point at infinity because (0,
// 0) is not on the any of the curves handled here.
func zForAffine(x, y *big.Int) *big.Int {
z := new(big.Int)
if x.Sign() != 0 || y.Sign() != 0 {
z.SetInt64(1)
}
return z
}
// affineFromJacobian reverses the Jacobian transform. See the comment at the // affineFromJacobian reverses the Jacobian transform. See the comment at the
// top of the file. // top of the file. If the point is ∞ it returns 0, 0.
func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
if z.Sign() == 0 {
return new(big.Int), new(big.Int)
}
zinv := new(big.Int).ModInverse(z, curve.P) zinv := new(big.Int).ModInverse(z, curve.P)
zinvsq := new(big.Int).Mul(zinv, zinv) zinvsq := new(big.Int).Mul(zinv, zinv)
@ -84,14 +99,29 @@ func (curve *CurveParams) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.
} }
func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { func (curve *CurveParams) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
z := new(big.Int).SetInt64(1) z1 := zForAffine(x1, y1)
return curve.affineFromJacobian(curve.addJacobian(x1, y1, z, x2, y2, z)) z2 := zForAffine(x2, y2)
return curve.affineFromJacobian(curve.addJacobian(x1, y1, z1, x2, y2, z2))
} }
// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and // addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
// (x2, y2, z2) and returns their sum, also in Jacobian form. // (x2, y2, z2) and returns their sum, also in Jacobian form.
func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl
x3, y3, z3 := new(big.Int), new(big.Int), new(big.Int)
if z1.Sign() == 0 {
x3.Set(x2)
y3.Set(y2)
z3.Set(z2)
return x3, y3, z3
}
if z2.Sign() == 0 {
x3.Set(x1)
y3.Set(y1)
z3.Set(z1)
return x3, y3, z3
}
z1z1 := new(big.Int).Mul(z1, z1) z1z1 := new(big.Int).Mul(z1, z1)
z1z1.Mod(z1z1, curve.P) z1z1.Mod(z1z1, curve.P)
z2z2 := new(big.Int).Mul(z2, z2) z2z2 := new(big.Int).Mul(z2, z2)
@ -102,6 +132,7 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
u2 := new(big.Int).Mul(x2, z1z1) u2 := new(big.Int).Mul(x2, z1z1)
u2.Mod(u2, curve.P) u2.Mod(u2, curve.P)
h := new(big.Int).Sub(u2, u1) h := new(big.Int).Sub(u2, u1)
xEqual := h.Sign() == 0
if h.Sign() == -1 { if h.Sign() == -1 {
h.Add(h, curve.P) h.Add(h, curve.P)
} }
@ -119,17 +150,21 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
if r.Sign() == -1 { if r.Sign() == -1 {
r.Add(r, curve.P) r.Add(r, curve.P)
} }
yEqual := r.Sign() == 0
if xEqual && yEqual {
return curve.doubleJacobian(x1, y1, z1)
}
r.Lsh(r, 1) r.Lsh(r, 1)
v := new(big.Int).Mul(u1, i) v := new(big.Int).Mul(u1, i)
x3 := new(big.Int).Set(r) x3.Set(r)
x3.Mul(x3, x3) x3.Mul(x3, x3)
x3.Sub(x3, j) x3.Sub(x3, j)
x3.Sub(x3, v) x3.Sub(x3, v)
x3.Sub(x3, v) x3.Sub(x3, v)
x3.Mod(x3, curve.P) x3.Mod(x3, curve.P)
y3 := new(big.Int).Set(r) y3.Set(r)
v.Sub(v, x3) v.Sub(v, x3)
y3.Mul(y3, v) y3.Mul(y3, v)
s1.Mul(s1, j) s1.Mul(s1, j)
@ -137,16 +172,10 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
y3.Sub(y3, s1) y3.Sub(y3, s1)
y3.Mod(y3, curve.P) y3.Mod(y3, curve.P)
z3 := new(big.Int).Add(z1, z2) z3.Add(z1, z2)
z3.Mul(z3, z3) z3.Mul(z3, z3)
z3.Sub(z3, z1z1) z3.Sub(z3, z1z1)
if z3.Sign() == -1 {
z3.Add(z3, curve.P)
}
z3.Sub(z3, z2z2) z3.Sub(z3, z2z2)
if z3.Sign() == -1 {
z3.Add(z3, curve.P)
}
z3.Mul(z3, h) z3.Mul(z3, h)
z3.Mod(z3, curve.P) z3.Mod(z3, curve.P)
@ -154,7 +183,7 @@ func (curve *CurveParams) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int
} }
func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { func (curve *CurveParams) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
z1 := new(big.Int).SetInt64(1) z1 := zForAffine(x1, y1)
return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1)) return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1))
} }
@ -219,40 +248,19 @@ func (curve *CurveParams) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int,
} }
func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { func (curve *CurveParams) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
// We have a slight problem in that the identity of the group (the
// point at infinity) cannot be represented in (x, y) form on a finite
// machine. Thus the standard add/double algorithm has to be tweaked
// slightly: our initial state is not the identity, but x, and we
// ignore the first true bit in |k|. If we don't find any true bits in
// |k|, then we return nil, nil, because we cannot return the identity
// element.
Bz := new(big.Int).SetInt64(1) Bz := new(big.Int).SetInt64(1)
x := Bx x, y, z := new(big.Int), new(big.Int), new(big.Int)
y := By
z := Bz
seenFirstTrue := false
for _, byte := range k { for _, byte := range k {
for bitNum := 0; bitNum < 8; bitNum++ { for bitNum := 0; bitNum < 8; bitNum++ {
if seenFirstTrue {
x, y, z = curve.doubleJacobian(x, y, z) x, y, z = curve.doubleJacobian(x, y, z)
}
if byte&0x80 == 0x80 { if byte&0x80 == 0x80 {
if !seenFirstTrue {
seenFirstTrue = true
} else {
x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z) x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z)
} }
}
byte <<= 1 byte <<= 1
} }
} }
if !seenFirstTrue {
return nil, nil
}
return curve.affineFromJacobian(x, y, z) return curve.affineFromJacobian(x, y, z)
} }

View File

@ -322,6 +322,44 @@ func TestGenericBaseMult(t *testing.T) {
} }
} }
func TestInfinity(t *testing.T) {
tests := []struct {
name string
curve Curve
}{
{"p224", P224()},
{"p256", P256()},
}
for _, test := range tests {
curve := test.curve
x, y := curve.ScalarBaseMult(nil)
if x.Sign() != 0 || y.Sign() != 0 {
t.Errorf("%s: x^0 != ∞", test.name)
}
x.SetInt64(0)
y.SetInt64(0)
x2, y2 := curve.Double(x, y)
if x2.Sign() != 0 || y2.Sign() != 0 {
t.Errorf("%s: 2∞ != ∞", test.name)
}
baseX := curve.Params().Gx
baseY := curve.Params().Gy
x3, y3 := curve.Add(baseX, baseY, x, y)
if x3.Cmp(baseX) != 0 || y3.Cmp(baseY) != 0 {
t.Errorf("%s: x+∞ != x", test.name)
}
x4, y4 := curve.Add(x, y, baseX, baseY)
if x4.Cmp(baseX) != 0 || y4.Cmp(baseY) != 0 {
t.Errorf("%s: ∞+x != x", test.name)
}
}
}
func BenchmarkBaseMult(b *testing.B) { func BenchmarkBaseMult(b *testing.B) {
b.ResetTimer() b.ResetTimer()
p224 := P224() p224 := P224()

View File

@ -80,10 +80,14 @@ func (p224Curve) Add(bigX1, bigY1, bigX2, bigY2 *big.Int) (x, y *big.Int) {
p224FromBig(&x1, bigX1) p224FromBig(&x1, bigX1)
p224FromBig(&y1, bigY1) p224FromBig(&y1, bigY1)
if bigX1.Sign() != 0 || bigY1.Sign() != 0 {
z1[0] = 1 z1[0] = 1
}
p224FromBig(&x2, bigX2) p224FromBig(&x2, bigX2)
p224FromBig(&y2, bigY2) p224FromBig(&y2, bigY2)
if bigX2.Sign() != 0 || bigY2.Sign() != 0 {
z2[0] = 1 z2[0] = 1
}
p224AddJacobian(&x3, &y3, &z3, &x1, &y1, &z1, &x2, &y2, &z2) p224AddJacobian(&x3, &y3, &z3, &x1, &y1, &z1, &x2, &y2, &z2)
return p224ToAffine(&x3, &y3, &z3) return p224ToAffine(&x3, &y3, &z3)
@ -132,6 +136,44 @@ func (curve p224Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
// exactly, making the reflections during a reduce much nicer. // exactly, making the reflections during a reduce much nicer.
type p224FieldElement [8]uint32 type p224FieldElement [8]uint32
// p224P is the order of the field, represented as a p224FieldElement.
var p224P = [8]uint32{1, 0, 0, 0xffff000, 0xfffffff, 0xfffffff, 0xfffffff, 0xfffffff}
// p224IsZero returns 1 if a == 0 mod p and 0 otherwise.
//
// a[i] < 2**29
func p224IsZero(a *p224FieldElement) uint32 {
// Since a p224FieldElement contains 224 bits there are two possible
// representations of 0: 0 and p.
var minimal p224FieldElement
p224Contract(&minimal, a)
var isZero, isP uint32
for i, v := range minimal {
isZero |= v
isP |= v - p224P[i]
}
// If either isZero or isP is 0, then we should return 1.
isZero |= isZero >> 16
isZero |= isZero >> 8
isZero |= isZero >> 4
isZero |= isZero >> 2
isZero |= isZero >> 1
isP |= isP >> 16
isP |= isP >> 8
isP |= isP >> 4
isP |= isP >> 2
isP |= isP >> 1
// For isZero and isP, the LSB is 0 iff all the bits are zero.
result := isZero & isP
result = (^result) & 1
return result
}
// p224Add computes *out = a+b // p224Add computes *out = a+b
// //
// a[i] + b[i] < 2**32 // a[i] + b[i] < 2**32
@ -406,7 +448,7 @@ func p224Contract(out, in *p224FieldElement) {
// true. // true.
top4AllOnes := uint32(0xffffffff) top4AllOnes := uint32(0xffffffff)
for i := 4; i < 8; i++ { for i := 4; i < 8; i++ {
top4AllOnes &= (out[i] & bottom28Bits) - 1 top4AllOnes &= out[i]
} }
top4AllOnes |= 0xf0000000 top4AllOnes |= 0xf0000000
// Now we replicate any zero bits to all the bits in top4AllOnes. // Now we replicate any zero bits to all the bits in top4AllOnes.
@ -441,7 +483,7 @@ func p224Contract(out, in *p224FieldElement) {
out3Equal = ^uint32(int32(out3Equal<<31) >> 31) out3Equal = ^uint32(int32(out3Equal<<31) >> 31)
// If out[3] > 0xffff000 then n's MSB will be zero. // If out[3] > 0xffff000 then n's MSB will be zero.
out3GT := ^uint32(int32(n<<31) >> 31) out3GT := ^uint32(int32(n) >> 31)
mask := top4AllOnes & ((out3Equal & bottom3NonZero) | out3GT) mask := top4AllOnes & ((out3Equal & bottom3NonZero) | out3GT)
out[0] -= 1 & mask out[0] -= 1 & mask
@ -463,6 +505,9 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
var z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v p224FieldElement var z1z1, z2z2, u1, u2, s1, s2, h, i, j, r, v p224FieldElement
var c p224LargeFieldElement var c p224LargeFieldElement
z1IsZero := p224IsZero(z1)
z2IsZero := p224IsZero(z2)
// Z1Z1 = Z1² // Z1Z1 = Z1²
p224Square(&z1z1, z1, &c) p224Square(&z1z1, z1, &c)
// Z2Z2 = Z2² // Z2Z2 = Z2²
@ -480,6 +525,7 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
// H = U2-U1 // H = U2-U1
p224Sub(&h, &u2, &u1) p224Sub(&h, &u2, &u1)
p224Reduce(&h) p224Reduce(&h)
xEqual := p224IsZero(&h)
// I = (2*H)² // I = (2*H)²
for j := 0; j < 8; j++ { for j := 0; j < 8; j++ {
i[j] = h[j] << 1 i[j] = h[j] << 1
@ -491,6 +537,11 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
// r = 2*(S2-S1) // r = 2*(S2-S1)
p224Sub(&r, &s2, &s1) p224Sub(&r, &s2, &s1)
p224Reduce(&r) p224Reduce(&r)
yEqual := p224IsZero(&r)
if xEqual == 1 && yEqual == 1 && z1IsZero == 0 && z2IsZero == 0 {
p224DoubleJacobian(x3, y3, z3, x1, y1, z1)
return
}
for i := 0; i < 8; i++ { for i := 0; i < 8; i++ {
r[i] <<= 1 r[i] <<= 1
} }
@ -524,6 +575,13 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
p224Mul(&z1z1, &z1z1, &r, &c) p224Mul(&z1z1, &z1z1, &r, &c)
p224Sub(y3, &z1z1, &s1) p224Sub(y3, &z1z1, &s1)
p224Reduce(y3) p224Reduce(y3)
p224CopyConditional(x3, x2, z1IsZero)
p224CopyConditional(x3, x1, z2IsZero)
p224CopyConditional(y3, y2, z1IsZero)
p224CopyConditional(y3, y1, z2IsZero)
p224CopyConditional(z3, z2, z1IsZero)
p224CopyConditional(z3, z1, z2IsZero)
} }
// p224DoubleJacobian computes *out = a+a. // p224DoubleJacobian computes *out = a+a.
@ -593,22 +651,19 @@ func p224CopyConditional(out, in *p224FieldElement, control uint32) {
func p224ScalarMult(outX, outY, outZ, inX, inY, inZ *p224FieldElement, scalar []byte) { func p224ScalarMult(outX, outY, outZ, inX, inY, inZ *p224FieldElement, scalar []byte) {
var xx, yy, zz p224FieldElement var xx, yy, zz p224FieldElement
for i := 0; i < 8; i++ { for i := 0; i < 8; i++ {
outX[i] = 0
outY[i] = 0
outZ[i] = 0 outZ[i] = 0
} }
firstBit := uint32(1)
for _, byte := range scalar { for _, byte := range scalar {
for bitNum := uint(0); bitNum < 8; bitNum++ { for bitNum := uint(0); bitNum < 8; bitNum++ {
p224DoubleJacobian(outX, outY, outZ, outX, outY, outZ) p224DoubleJacobian(outX, outY, outZ, outX, outY, outZ)
bit := uint32((byte >> (7 - bitNum)) & 1) bit := uint32((byte >> (7 - bitNum)) & 1)
p224AddJacobian(&xx, &yy, &zz, inX, inY, inZ, outX, outY, outZ) p224AddJacobian(&xx, &yy, &zz, inX, inY, inZ, outX, outY, outZ)
p224CopyConditional(outX, inX, firstBit&bit) p224CopyConditional(outX, &xx, bit)
p224CopyConditional(outY, inY, firstBit&bit) p224CopyConditional(outY, &yy, bit)
p224CopyConditional(outZ, inZ, firstBit&bit) p224CopyConditional(outZ, &zz, bit)
p224CopyConditional(outX, &xx, ^firstBit&bit)
p224CopyConditional(outY, &yy, ^firstBit&bit)
p224CopyConditional(outZ, &zz, ^firstBit&bit)
firstBit = firstBit & ^bit
} }
} }
} }
@ -618,16 +673,8 @@ func p224ToAffine(x, y, z *p224FieldElement) (*big.Int, *big.Int) {
var zinv, zinvsq, outx, outy p224FieldElement var zinv, zinvsq, outx, outy p224FieldElement
var tmp p224LargeFieldElement var tmp p224LargeFieldElement
isPointAtInfinity := true if isPointAtInfinity := p224IsZero(z); isPointAtInfinity == 1 {
for i := 0; i < 8; i++ { return new(big.Int), new(big.Int)
if z[i] != 0 {
isPointAtInfinity = false
break
}
}
if isPointAtInfinity {
return nil, nil
} }
p224Invert(&zinv, z) p224Invert(&zinv, z)

298
libgo/go/crypto/md5/gen.go Normal file
View File

@ -0,0 +1,298 @@
// Copyright 2012 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.
// +build ignore
// This program generates md5block.go
// Invoke as
//
// go run gen.go [-full] |gofmt >md5block.go
//
// The -full flag causes the generated code to do a full
// (16x) unrolling instead of a 4x unrolling.
package main
import (
"flag"
"log"
"os"
"strings"
"text/template"
)
func main() {
flag.Parse()
t := template.Must(template.New("main").Funcs(funcs).Parse(program))
if err := t.Execute(os.Stdout, data); err != nil {
log.Fatal(err)
}
}
type Data struct {
a, b, c, d string
Shift1 []int
Shift2 []int
Shift3 []int
Shift4 []int
Table1 []uint32
Table2 []uint32
Table3 []uint32
Table4 []uint32
Full bool
}
var funcs = template.FuncMap{
"dup": dup,
"relabel": relabel,
"rotate": rotate,
}
func dup(count int, x []int) []int {
var out []int
for i := 0; i < count; i++ {
out = append(out, x...)
}
return out
}
func relabel(s string) string {
return strings.NewReplacer("a", data.a, "b", data.b, "c", data.c, "d", data.d).Replace(s)
}
func rotate() string {
data.a, data.b, data.c, data.d = data.d, data.a, data.b, data.c
return "" // no output
}
func init() {
flag.BoolVar(&data.Full, "full", false, "complete unrolling")
}
var data = Data{
a: "a",
b: "b",
c: "c",
d: "d",
Shift1: []int{7, 12, 17, 22},
Shift2: []int{5, 9, 14, 20},
Shift3: []int{4, 11, 16, 23},
Shift4: []int{6, 10, 15, 21},
// table[i] = int((1<<32) * abs(sin(i+1 radians))).
Table1: []uint32{
// round 1
0xd76aa478,
0xe8c7b756,
0x242070db,
0xc1bdceee,
0xf57c0faf,
0x4787c62a,
0xa8304613,
0xfd469501,
0x698098d8,
0x8b44f7af,
0xffff5bb1,
0x895cd7be,
0x6b901122,
0xfd987193,
0xa679438e,
0x49b40821,
},
Table2: []uint32{
// round 2
0xf61e2562,
0xc040b340,
0x265e5a51,
0xe9b6c7aa,
0xd62f105d,
0x2441453,
0xd8a1e681,
0xe7d3fbc8,
0x21e1cde6,
0xc33707d6,
0xf4d50d87,
0x455a14ed,
0xa9e3e905,
0xfcefa3f8,
0x676f02d9,
0x8d2a4c8a,
},
Table3: []uint32{
// round3
0xfffa3942,
0x8771f681,
0x6d9d6122,
0xfde5380c,
0xa4beea44,
0x4bdecfa9,
0xf6bb4b60,
0xbebfbc70,
0x289b7ec6,
0xeaa127fa,
0xd4ef3085,
0x4881d05,
0xd9d4d039,
0xe6db99e5,
0x1fa27cf8,
0xc4ac5665,
},
Table4: []uint32{
// round 4
0xf4292244,
0x432aff97,
0xab9423a7,
0xfc93a039,
0x655b59c3,
0x8f0ccc92,
0xffeff47d,
0x85845dd1,
0x6fa87e4f,
0xfe2ce6e0,
0xa3014314,
0x4e0811a1,
0xf7537e82,
0xbd3af235,
0x2ad7d2bb,
0xeb86d391,
},
}
var program = `
package md5
import (
"unsafe"
"runtime"
)
{{if not .Full}}
var t1 = [...]uint32{
{{range .Table1}}{{printf "\t%#x,\n" .}}{{end}}
}
var t2 = [...]uint32{
{{range .Table2}}{{printf "\t%#x,\n" .}}{{end}}
}
var t3 = [...]uint32{
{{range .Table3}}{{printf "\t%#x,\n" .}}{{end}}
}
var t4 = [...]uint32{
{{range .Table4}}{{printf "\t%#x,\n" .}}{{end}}
}
{{end}}
func block(dig *digest, p []byte) {
a := dig.s[0]
b := dig.s[1]
c := dig.s[2]
d := dig.s[3]
var X *[16]uint32
var xbuf [16]uint32
for len(p) >= chunk {
aa, bb, cc, dd := a, b, c, d
// This is a constant condition - it is not evaluated on each iteration.
if runtime.GOARCH == "amd64" || runtime.GOARCH == "386" {
// MD5 was designed so that x86 processors can just iterate
// over the block data directly as uint32s, and we generate
// less code and run 1.3x faster if we take advantage of that.
// My apologies.
X = (*[16]uint32)(unsafe.Pointer(&p[0]))
} else {
X = &xbuf
j := 0
for i := 0; i < 16; i++ {
X[i&15] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24
j += 4
}
}
{{if .Full}}
// Round 1.
{{range $i, $s := dup 4 .Shift1}}
{{index $.Table1 $i | printf "a += (((c^d)&b)^d) + X[%d] + %d" $i | relabel}}
{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
{{rotate}}
{{end}}
// Round 2.
{{range $i, $s := dup 4 .Shift2}}
{{index $.Table2 $i | printf "a += (((b^c)&d)^c) + X[(1+5*%d)&15] + %d" $i | relabel}}
{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
{{rotate}}
{{end}}
// Round 3.
{{range $i, $s := dup 4 .Shift3}}
{{index $.Table3 $i | printf "a += (b^c^d) + X[(5+3*%d)&15] + %d" $i | relabel}}
{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
{{rotate}}
{{end}}
// Round 4.
{{range $i, $s := dup 4 .Shift4}}
{{index $.Table4 $i | printf "a += (c^(b|^d)) + X[(7*%d)&15] + %d" $i | relabel}}
{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
{{rotate}}
{{end}}
{{else}}
// Round 1.
for i := uint(0); i < 16; {
{{range $s := .Shift1}}
{{printf "a += (((c^d)&b)^d) + X[i&15] + t1[i&15]" | relabel}}
{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
i++
{{rotate}}
{{end}}
}
// Round 2.
for i := uint(0); i < 16; {
{{range $s := .Shift2}}
{{printf "a += (((b^c)&d)^c) + X[(1+5*i)&15] + t2[i&15]" | relabel}}
{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
i++
{{rotate}}
{{end}}
}
// Round 3.
for i := uint(0); i < 16; {
{{range $s := .Shift3}}
{{printf "a += (b^c^d) + X[(5+3*i)&15] + t3[i&15]" | relabel}}
{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
i++
{{rotate}}
{{end}}
}
// Round 4.
for i := uint(0); i < 16; {
{{range $s := .Shift4}}
{{printf "a += (c^(b|^d)) + X[(7*i)&15] + t4[i&15]" | relabel}}
{{printf "a = a<<%d | a>>(32-%d) + b" $s $s | relabel}}
i++
{{rotate}}
{{end}}
}
{{end}}
a += aa
b += bb
c += cc
d += dd
p = p[chunk:]
}
dig.s[0] = a
dig.s[1] = b
dig.s[2] = c
dig.s[3] = d
}
`

View File

@ -21,26 +21,26 @@ const Size = 16
const BlockSize = 64 const BlockSize = 64
const ( const (
_Chunk = 64 chunk = 64
_Init0 = 0x67452301 init0 = 0x67452301
_Init1 = 0xEFCDAB89 init1 = 0xEFCDAB89
_Init2 = 0x98BADCFE init2 = 0x98BADCFE
_Init3 = 0x10325476 init3 = 0x10325476
) )
// digest represents the partial evaluation of a checksum. // digest represents the partial evaluation of a checksum.
type digest struct { type digest struct {
s [4]uint32 s [4]uint32
x [_Chunk]byte x [chunk]byte
nx int nx int
len uint64 len uint64
} }
func (d *digest) Reset() { func (d *digest) Reset() {
d.s[0] = _Init0 d.s[0] = init0
d.s[1] = _Init1 d.s[1] = init1
d.s[2] = _Init2 d.s[2] = init2
d.s[3] = _Init3 d.s[3] = init3
d.nx = 0 d.nx = 0
d.len = 0 d.len = 0
} }
@ -61,21 +61,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
d.len += uint64(nn) d.len += uint64(nn)
if d.nx > 0 { if d.nx > 0 {
n := len(p) n := len(p)
if n > _Chunk-d.nx { if n > chunk-d.nx {
n = _Chunk - d.nx n = chunk - d.nx
} }
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
d.x[d.nx+i] = p[i] d.x[d.nx+i] = p[i]
} }
d.nx += n d.nx += n
if d.nx == _Chunk { if d.nx == chunk {
_Block(d, d.x[0:]) block(d, d.x[0:chunk])
d.nx = 0 d.nx = 0
} }
p = p[n:] p = p[n:]
} }
n := _Block(d, p) if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
block(d, p[:n])
p = p[n:] p = p[n:]
}
if len(p) > 0 { if len(p) > 0 {
d.nx = copy(d.x[:], p) d.nx = copy(d.x[:], p)
} }

View File

@ -78,3 +78,28 @@ func ExampleNew() {
fmt.Printf("%x", h.Sum(nil)) fmt.Printf("%x", h.Sum(nil))
// Output: e2c569be17396eca2a2e3c11578123ed // Output: e2c569be17396eca2a2e3c11578123ed
} }
var bench = md5.New()
var buf = makeBuf()
func makeBuf() []byte {
b := make([]byte, 8<<10)
for i := range b {
b[i] = byte(i)
}
return b
}
func BenchmarkHash1K(b *testing.B) {
b.SetBytes(1024)
for i := 0; i < b.N; i++ {
bench.Write(buf[:1024])
}
}
func BenchmarkHash8K(b *testing.B) {
b.SetBytes(int64(len(buf)))
for i := 0; i < b.N; i++ {
bench.Write(buf)
}
}

View File

@ -1,172 +1,246 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// MD5 block step.
// In its own file so that a faster assembly or C version
// can be substituted easily.
package md5 package md5
// table[i] = int((1<<32) * abs(sin(i+1 radians))). import (
var table = []uint32{ "runtime"
// round 1 "unsafe"
0xd76aa478, )
0xe8c7b756,
0x242070db,
0xc1bdceee,
0xf57c0faf,
0x4787c62a,
0xa8304613,
0xfd469501,
0x698098d8,
0x8b44f7af,
0xffff5bb1,
0x895cd7be,
0x6b901122,
0xfd987193,
0xa679438e,
0x49b40821,
// round 2 func block(dig *digest, p []byte) {
0xf61e2562,
0xc040b340,
0x265e5a51,
0xe9b6c7aa,
0xd62f105d,
0x2441453,
0xd8a1e681,
0xe7d3fbc8,
0x21e1cde6,
0xc33707d6,
0xf4d50d87,
0x455a14ed,
0xa9e3e905,
0xfcefa3f8,
0x676f02d9,
0x8d2a4c8a,
// round3
0xfffa3942,
0x8771f681,
0x6d9d6122,
0xfde5380c,
0xa4beea44,
0x4bdecfa9,
0xf6bb4b60,
0xbebfbc70,
0x289b7ec6,
0xeaa127fa,
0xd4ef3085,
0x4881d05,
0xd9d4d039,
0xe6db99e5,
0x1fa27cf8,
0xc4ac5665,
// round 4
0xf4292244,
0x432aff97,
0xab9423a7,
0xfc93a039,
0x655b59c3,
0x8f0ccc92,
0xffeff47d,
0x85845dd1,
0x6fa87e4f,
0xfe2ce6e0,
0xa3014314,
0x4e0811a1,
0xf7537e82,
0xbd3af235,
0x2ad7d2bb,
0xeb86d391,
}
var shift1 = []uint{7, 12, 17, 22}
var shift2 = []uint{5, 9, 14, 20}
var shift3 = []uint{4, 11, 16, 23}
var shift4 = []uint{6, 10, 15, 21}
func _Block(dig *digest, p []byte) int {
a := dig.s[0] a := dig.s[0]
b := dig.s[1] b := dig.s[1]
c := dig.s[2] c := dig.s[2]
d := dig.s[3] d := dig.s[3]
n := 0 var X *[16]uint32
var X [16]uint32 var xbuf [16]uint32
for len(p) >= _Chunk { for len(p) >= chunk {
aa, bb, cc, dd := a, b, c, d aa, bb, cc, dd := a, b, c, d
// This is a constant condition - it is not evaluated on each iteration.
if runtime.GOARCH == "amd64" || runtime.GOARCH == "386" {
// MD5 was designed so that x86 processors can just iterate
// over the block data directly as uint32s, and we generate
// less code and run 1.3x faster if we take advantage of that.
// My apologies.
X = (*[16]uint32)(unsafe.Pointer(&p[0]))
} else {
X = &xbuf
j := 0 j := 0
for i := 0; i < 16; i++ { for i := 0; i < 16; i++ {
X[i] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24 X[i&15] = uint32(p[j]) | uint32(p[j+1])<<8 | uint32(p[j+2])<<16 | uint32(p[j+3])<<24
j += 4 j += 4
} }
}
// If this needs to be made faster in the future,
// the usual trick is to unroll each of these
// loops by a factor of 4; that lets you replace
// the shift[] lookups with constants and,
// with suitable variable renaming in each
// unrolled body, delete the a, b, c, d = d, a, b, c
// (or you can let the optimizer do the renaming).
//
// The index variables are uint so that % by a power
// of two can be optimized easily by a compiler.
// Round 1. // Round 1.
for i := uint(0); i < 16; i++ {
x := i a += (((c ^ d) & b) ^ d) + X[0] + 3614090360
s := shift1[i%4] a = a<<7 | a>>(32-7) + b
f := ((c ^ d) & b) ^ d
a += f + X[x] + table[i] d += (((b ^ c) & a) ^ c) + X[1] + 3905402710
a = a<<s | a>>(32-s) + b d = d<<12 | d>>(32-12) + a
a, b, c, d = d, a, b, c
} c += (((a ^ b) & d) ^ b) + X[2] + 606105819
c = c<<17 | c>>(32-17) + d
b += (((d ^ a) & c) ^ a) + X[3] + 3250441966
b = b<<22 | b>>(32-22) + c
a += (((c ^ d) & b) ^ d) + X[4] + 4118548399
a = a<<7 | a>>(32-7) + b
d += (((b ^ c) & a) ^ c) + X[5] + 1200080426
d = d<<12 | d>>(32-12) + a
c += (((a ^ b) & d) ^ b) + X[6] + 2821735955
c = c<<17 | c>>(32-17) + d
b += (((d ^ a) & c) ^ a) + X[7] + 4249261313
b = b<<22 | b>>(32-22) + c
a += (((c ^ d) & b) ^ d) + X[8] + 1770035416
a = a<<7 | a>>(32-7) + b
d += (((b ^ c) & a) ^ c) + X[9] + 2336552879
d = d<<12 | d>>(32-12) + a
c += (((a ^ b) & d) ^ b) + X[10] + 4294925233
c = c<<17 | c>>(32-17) + d
b += (((d ^ a) & c) ^ a) + X[11] + 2304563134
b = b<<22 | b>>(32-22) + c
a += (((c ^ d) & b) ^ d) + X[12] + 1804603682
a = a<<7 | a>>(32-7) + b
d += (((b ^ c) & a) ^ c) + X[13] + 4254626195
d = d<<12 | d>>(32-12) + a
c += (((a ^ b) & d) ^ b) + X[14] + 2792965006
c = c<<17 | c>>(32-17) + d
b += (((d ^ a) & c) ^ a) + X[15] + 1236535329
b = b<<22 | b>>(32-22) + c
// Round 2. // Round 2.
for i := uint(0); i < 16; i++ {
x := (1 + 5*i) % 16 a += (((b ^ c) & d) ^ c) + X[(1+5*0)&15] + 4129170786
s := shift2[i%4] a = a<<5 | a>>(32-5) + b
g := ((b ^ c) & d) ^ c
a += g + X[x] + table[i+16] d += (((a ^ b) & c) ^ b) + X[(1+5*1)&15] + 3225465664
a = a<<s | a>>(32-s) + b d = d<<9 | d>>(32-9) + a
a, b, c, d = d, a, b, c
} c += (((d ^ a) & b) ^ a) + X[(1+5*2)&15] + 643717713
c = c<<14 | c>>(32-14) + d
b += (((c ^ d) & a) ^ d) + X[(1+5*3)&15] + 3921069994
b = b<<20 | b>>(32-20) + c
a += (((b ^ c) & d) ^ c) + X[(1+5*4)&15] + 3593408605
a = a<<5 | a>>(32-5) + b
d += (((a ^ b) & c) ^ b) + X[(1+5*5)&15] + 38016083
d = d<<9 | d>>(32-9) + a
c += (((d ^ a) & b) ^ a) + X[(1+5*6)&15] + 3634488961
c = c<<14 | c>>(32-14) + d
b += (((c ^ d) & a) ^ d) + X[(1+5*7)&15] + 3889429448
b = b<<20 | b>>(32-20) + c
a += (((b ^ c) & d) ^ c) + X[(1+5*8)&15] + 568446438
a = a<<5 | a>>(32-5) + b
d += (((a ^ b) & c) ^ b) + X[(1+5*9)&15] + 3275163606
d = d<<9 | d>>(32-9) + a
c += (((d ^ a) & b) ^ a) + X[(1+5*10)&15] + 4107603335
c = c<<14 | c>>(32-14) + d
b += (((c ^ d) & a) ^ d) + X[(1+5*11)&15] + 1163531501
b = b<<20 | b>>(32-20) + c
a += (((b ^ c) & d) ^ c) + X[(1+5*12)&15] + 2850285829
a = a<<5 | a>>(32-5) + b
d += (((a ^ b) & c) ^ b) + X[(1+5*13)&15] + 4243563512
d = d<<9 | d>>(32-9) + a
c += (((d ^ a) & b) ^ a) + X[(1+5*14)&15] + 1735328473
c = c<<14 | c>>(32-14) + d
b += (((c ^ d) & a) ^ d) + X[(1+5*15)&15] + 2368359562
b = b<<20 | b>>(32-20) + c
// Round 3. // Round 3.
for i := uint(0); i < 16; i++ {
x := (5 + 3*i) % 16 a += (b ^ c ^ d) + X[(5+3*0)&15] + 4294588738
s := shift3[i%4] a = a<<4 | a>>(32-4) + b
h := b ^ c ^ d
a += h + X[x] + table[i+32] d += (a ^ b ^ c) + X[(5+3*1)&15] + 2272392833
a = a<<s | a>>(32-s) + b d = d<<11 | d>>(32-11) + a
a, b, c, d = d, a, b, c
} c += (d ^ a ^ b) + X[(5+3*2)&15] + 1839030562
c = c<<16 | c>>(32-16) + d
b += (c ^ d ^ a) + X[(5+3*3)&15] + 4259657740
b = b<<23 | b>>(32-23) + c
a += (b ^ c ^ d) + X[(5+3*4)&15] + 2763975236
a = a<<4 | a>>(32-4) + b
d += (a ^ b ^ c) + X[(5+3*5)&15] + 1272893353
d = d<<11 | d>>(32-11) + a
c += (d ^ a ^ b) + X[(5+3*6)&15] + 4139469664
c = c<<16 | c>>(32-16) + d
b += (c ^ d ^ a) + X[(5+3*7)&15] + 3200236656
b = b<<23 | b>>(32-23) + c
a += (b ^ c ^ d) + X[(5+3*8)&15] + 681279174
a = a<<4 | a>>(32-4) + b
d += (a ^ b ^ c) + X[(5+3*9)&15] + 3936430074
d = d<<11 | d>>(32-11) + a
c += (d ^ a ^ b) + X[(5+3*10)&15] + 3572445317
c = c<<16 | c>>(32-16) + d
b += (c ^ d ^ a) + X[(5+3*11)&15] + 76029189
b = b<<23 | b>>(32-23) + c
a += (b ^ c ^ d) + X[(5+3*12)&15] + 3654602809
a = a<<4 | a>>(32-4) + b
d += (a ^ b ^ c) + X[(5+3*13)&15] + 3873151461
d = d<<11 | d>>(32-11) + a
c += (d ^ a ^ b) + X[(5+3*14)&15] + 530742520
c = c<<16 | c>>(32-16) + d
b += (c ^ d ^ a) + X[(5+3*15)&15] + 3299628645
b = b<<23 | b>>(32-23) + c
// Round 4. // Round 4.
for i := uint(0); i < 16; i++ {
x := (7 * i) % 16 a += (c ^ (b | ^d)) + X[(7*0)&15] + 4096336452
s := shift4[i%4] a = a<<6 | a>>(32-6) + b
j := c ^ (b | ^d)
a += j + X[x] + table[i+48] d += (b ^ (a | ^c)) + X[(7*1)&15] + 1126891415
a = a<<s | a>>(32-s) + b d = d<<10 | d>>(32-10) + a
a, b, c, d = d, a, b, c
} c += (a ^ (d | ^b)) + X[(7*2)&15] + 2878612391
c = c<<15 | c>>(32-15) + d
b += (d ^ (c | ^a)) + X[(7*3)&15] + 4237533241
b = b<<21 | b>>(32-21) + c
a += (c ^ (b | ^d)) + X[(7*4)&15] + 1700485571
a = a<<6 | a>>(32-6) + b
d += (b ^ (a | ^c)) + X[(7*5)&15] + 2399980690
d = d<<10 | d>>(32-10) + a
c += (a ^ (d | ^b)) + X[(7*6)&15] + 4293915773
c = c<<15 | c>>(32-15) + d
b += (d ^ (c | ^a)) + X[(7*7)&15] + 2240044497
b = b<<21 | b>>(32-21) + c
a += (c ^ (b | ^d)) + X[(7*8)&15] + 1873313359
a = a<<6 | a>>(32-6) + b
d += (b ^ (a | ^c)) + X[(7*9)&15] + 4264355552
d = d<<10 | d>>(32-10) + a
c += (a ^ (d | ^b)) + X[(7*10)&15] + 2734768916
c = c<<15 | c>>(32-15) + d
b += (d ^ (c | ^a)) + X[(7*11)&15] + 1309151649
b = b<<21 | b>>(32-21) + c
a += (c ^ (b | ^d)) + X[(7*12)&15] + 4149444226
a = a<<6 | a>>(32-6) + b
d += (b ^ (a | ^c)) + X[(7*13)&15] + 3174756917
d = d<<10 | d>>(32-10) + a
c += (a ^ (d | ^b)) + X[(7*14)&15] + 718787259
c = c<<15 | c>>(32-15) + d
b += (d ^ (c | ^a)) + X[(7*15)&15] + 3951481745
b = b<<21 | b>>(32-21) + c
a += aa a += aa
b += bb b += bb
c += cc c += cc
d += dd d += dd
p = p[_Chunk:] p = p[chunk:]
n += _Chunk
} }
dig.s[0] = a dig.s[0] = a
dig.s[1] = b dig.s[1] = b
dig.s[2] = c dig.s[2] = c
dig.s[3] = d dig.s[3] = d
return n
} }

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 // +build darwin freebsd linux netbsd openbsd plan9
// Unix cryptographically secure pseudorandom number // Unix cryptographically secure pseudorandom number
// generator. // generator.
@ -15,6 +15,7 @@ import (
"crypto/cipher" "crypto/cipher"
"io" "io"
"os" "os"
"runtime"
"sync" "sync"
"time" "time"
) )
@ -22,7 +23,13 @@ import (
// Easy implementation: read from /dev/urandom. // Easy implementation: read from /dev/urandom.
// This is sufficient on Linux, OS X, and FreeBSD. // This is sufficient on Linux, OS X, and FreeBSD.
func init() { Reader = &devReader{name: "/dev/urandom"} } func init() {
if runtime.GOOS == "plan9" {
Reader = newReader(nil)
} else {
Reader = &devReader{name: "/dev/urandom"}
}
}
// A devReader satisfies reads by reading the file named name. // A devReader satisfies reads by reading the file named name.
type devReader struct { type devReader struct {
@ -39,14 +46,17 @@ func (r *devReader) Read(b []byte) (n int, err error) {
if f == nil { if f == nil {
return 0, err return 0, err
} }
if runtime.GOOS == "plan9" {
r.f = f
} else {
r.f = bufio.NewReader(f) r.f = bufio.NewReader(f)
} }
}
return r.f.Read(b) return r.f.Read(b)
} }
// Alternate pseudo-random implementation for use on // Alternate pseudo-random implementation for use on
// systems without a reliable /dev/urandom. So far we // systems without a reliable /dev/urandom.
// haven't needed it.
// newReader returns a new pseudorandom generator that // newReader returns a new pseudorandom generator that
// seeds itself by reading from entropy. If entropy == nil, // seeds itself by reading from entropy. If entropy == nil,

View File

@ -19,6 +19,9 @@ import (
// WARNING: use of this function to encrypt plaintexts other than session keys // WARNING: use of this function to encrypt plaintexts other than session keys
// is dangerous. Use RSA OAEP in new protocols. // is dangerous. Use RSA OAEP in new protocols.
func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error) { func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error) {
if err := checkPub(pub); err != nil {
return nil, err
}
k := (pub.N.BitLen() + 7) / 8 k := (pub.N.BitLen() + 7) / 8
if len(msg) > k-11 { if len(msg) > k-11 {
err = ErrMessageTooLong err = ErrMessageTooLong
@ -47,6 +50,9 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, er
// DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5. // DecryptPKCS1v15 decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5.
// If rand != nil, it uses RSA blinding to avoid timing side-channel attacks. // If rand != nil, it uses RSA blinding to avoid timing side-channel attacks.
func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error) { func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error) {
if err := checkPub(&priv.PublicKey); err != nil {
return nil, err
}
valid, out, err := decryptPKCS1v15(rand, priv, ciphertext) valid, out, err := decryptPKCS1v15(rand, priv, ciphertext)
if err == nil && valid == 0 { if err == nil && valid == 0 {
err = ErrDecryption err = ErrDecryption
@ -69,6 +75,9 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out [
// Encryption Standard PKCS #1'', Daniel Bleichenbacher, Advances in Cryptology // Encryption Standard PKCS #1'', Daniel Bleichenbacher, Advances in Cryptology
// (Crypto '98). // (Crypto '98).
func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) (err error) { func DecryptPKCS1v15SessionKey(rand io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) (err error) {
if err := checkPub(&priv.PublicKey); err != nil {
return err
}
k := (priv.N.BitLen() + 7) / 8 k := (priv.N.BitLen() + 7) / 8
if k-(len(key)+3+8) < 0 { if k-(len(key)+3+8) < 0 {
err = ErrDecryption err = ErrDecryption
@ -238,11 +247,11 @@ func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte)
func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) { func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) {
hashLen = hash.Size() hashLen = hash.Size()
if inLen != hashLen { if inLen != hashLen {
return 0, nil, errors.New("input must be hashed message") return 0, nil, errors.New("crypto/rsa: input must be hashed message")
} }
prefix, ok := hashPrefixes[hash] prefix, ok := hashPrefixes[hash]
if !ok { if !ok {
return 0, nil, errors.New("unsupported hash function") return 0, nil, errors.New("crypto/rsa: unsupported hash function")
} }
return return
} }

View File

@ -25,6 +25,30 @@ type PublicKey struct {
E int // public exponent E int // public exponent
} }
var (
errPublicModulus = errors.New("crypto/rsa: missing public modulus")
errPublicExponentSmall = errors.New("crypto/rsa: public exponent too small")
errPublicExponentLarge = errors.New("crypto/rsa: public exponent too large")
)
// checkPub sanity checks the public key before we use it.
// We require pub.E to fit into a 32-bit integer so that we
// do not have different behavior depending on whether
// int is 32 or 64 bits. See also
// http://www.imperialviolet.org/2012/03/16/rsae.html.
func checkPub(pub *PublicKey) error {
if pub.N == nil {
return errPublicModulus
}
if pub.E < 2 {
return errPublicExponentSmall
}
if pub.E > 1<<31-1 {
return errPublicExponentLarge
}
return nil
}
// A PrivateKey represents an RSA key // A PrivateKey represents an RSA key
type PrivateKey struct { type PrivateKey struct {
PublicKey // public part. PublicKey // public part.
@ -57,13 +81,17 @@ type CRTValue struct {
// Validate performs basic sanity checks on the key. // Validate performs basic sanity checks on the key.
// It returns nil if the key is valid, or else an error describing a problem. // It returns nil if the key is valid, or else an error describing a problem.
func (priv *PrivateKey) Validate() error { func (priv *PrivateKey) Validate() error {
if err := checkPub(&priv.PublicKey); err != nil {
return err
}
// Check that the prime factors are actually prime. Note that this is // Check that the prime factors are actually prime. Note that this is
// just a sanity check. Since the random witnesses chosen by // just a sanity check. Since the random witnesses chosen by
// ProbablyPrime are deterministic, given the candidate number, it's // ProbablyPrime are deterministic, given the candidate number, it's
// easy for an attack to generate composites that pass this test. // easy for an attack to generate composites that pass this test.
for _, prime := range priv.Primes { for _, prime := range priv.Primes {
if !prime.ProbablyPrime(20) { if !prime.ProbablyPrime(20) {
return errors.New("prime factor is composite") return errors.New("crypto/rsa: prime factor is composite")
} }
} }
@ -73,27 +101,23 @@ func (priv *PrivateKey) Validate() error {
modulus.Mul(modulus, prime) modulus.Mul(modulus, prime)
} }
if modulus.Cmp(priv.N) != 0 { if modulus.Cmp(priv.N) != 0 {
return errors.New("invalid modulus") return errors.New("crypto/rsa: invalid modulus")
} }
// Check that e and totient(Πprimes) are coprime.
totient := new(big.Int).Set(bigOne) // Check that de ≡ 1 mod p-1, for each prime.
// This implies that e is coprime to each p-1 as e has a multiplicative
// inverse. Therefore e is coprime to lcm(p-1,q-1,r-1,...) =
// exponent(/n). It also implies that a^de ≡ a mod p as a^(p-1) ≡ 1
// mod p. Thus a^de ≡ a mod n for all a coprime to n, as required.
congruence := new(big.Int)
de := new(big.Int).SetInt64(int64(priv.E))
de.Mul(de, priv.D)
for _, prime := range priv.Primes { for _, prime := range priv.Primes {
pminus1 := new(big.Int).Sub(prime, bigOne) pminus1 := new(big.Int).Sub(prime, bigOne)
totient.Mul(totient, pminus1) congruence.Mod(de, pminus1)
if congruence.Cmp(bigOne) != 0 {
return errors.New("crypto/rsa: invalid exponents")
} }
e := big.NewInt(int64(priv.E))
gcd := new(big.Int)
x := new(big.Int)
y := new(big.Int)
gcd.GCD(x, y, totient, e)
if gcd.Cmp(bigOne) != 0 {
return errors.New("invalid public exponent E")
}
// Check that de ≡ 1 (mod totient(Πprimes))
de := new(big.Int).Mul(priv.D, e)
de.Mod(de, totient)
if de.Cmp(bigOne) != 0 {
return errors.New("invalid private exponent D")
} }
return nil return nil
} }
@ -118,7 +142,7 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *Priva
priv.E = 65537 priv.E = 65537
if nprimes < 2 { if nprimes < 2 {
return nil, errors.New("rsa.GenerateMultiPrimeKey: nprimes must be >= 2") return nil, errors.New("crypto/rsa: GenerateMultiPrimeKey: nprimes must be >= 2")
} }
primes := make([]*big.Int, nprimes) primes := make([]*big.Int, nprimes)
@ -220,6 +244,9 @@ func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int {
// The message must be no longer than the length of the public modulus less // The message must be no longer than the length of the public modulus less
// twice the hash length plus 2. // twice the hash length plus 2.
func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err error) { func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) (out []byte, err error) {
if err := checkPub(pub); err != nil {
return nil, err
}
hash.Reset() hash.Reset()
k := (pub.N.BitLen() + 7) / 8 k := (pub.N.BitLen() + 7) / 8
if len(msg) > k-2*hash.Size()-2 { if len(msg) > k-2*hash.Size()-2 {
@ -406,6 +433,9 @@ func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err er
// DecryptOAEP decrypts ciphertext using RSA-OAEP. // DecryptOAEP decrypts ciphertext using RSA-OAEP.
// If random != nil, DecryptOAEP uses RSA blinding to avoid timing side-channel attacks. // If random != nil, DecryptOAEP uses RSA blinding to avoid timing side-channel attacks.
func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err error) { func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) (msg []byte, err error) {
if err := checkPub(&priv.PublicKey); err != nil {
return nil, err
}
k := (priv.N.BitLen() + 7) / 8 k := (priv.N.BitLen() + 7) / 8
if len(ciphertext) > k || if len(ciphertext) > k ||
k < hash.Size()*2+2 { k < hash.Size()*2+2 {

View File

@ -50,6 +50,24 @@ func Test4PrimeKeyGeneration(t *testing.T) {
testKeyBasics(t, priv) testKeyBasics(t, priv)
} }
func TestGnuTLSKey(t *testing.T) {
// This is a key generated by `certtool --generate-privkey --bits 128`.
// It's such that de ≢ 1 mod φ(n), but is congruent mod the order of
// the group.
priv := &PrivateKey{
PublicKey: PublicKey{
N: fromBase10("290684273230919398108010081414538931343"),
E: 65537,
},
D: fromBase10("31877380284581499213530787347443987241"),
Primes: []*big.Int{
fromBase10("16775196964030542637"),
fromBase10("17328218193455850539"),
},
}
testKeyBasics(t, priv)
}
func testKeyBasics(t *testing.T, priv *PrivateKey) { func testKeyBasics(t *testing.T, priv *PrivateKey) {
if err := priv.Validate(); err != nil { if err := priv.Validate(); err != nil {
t.Errorf("Validate() failed: %s", err) t.Errorf("Validate() failed: %s", err)

View File

@ -21,28 +21,28 @@ const Size = 20
const BlockSize = 64 const BlockSize = 64
const ( const (
_Chunk = 64 chunk = 64
_Init0 = 0x67452301 init0 = 0x67452301
_Init1 = 0xEFCDAB89 init1 = 0xEFCDAB89
_Init2 = 0x98BADCFE init2 = 0x98BADCFE
_Init3 = 0x10325476 init3 = 0x10325476
_Init4 = 0xC3D2E1F0 init4 = 0xC3D2E1F0
) )
// digest represents the partial evaluation of a checksum. // digest represents the partial evaluation of a checksum.
type digest struct { type digest struct {
h [5]uint32 h [5]uint32
x [_Chunk]byte x [chunk]byte
nx int nx int
len uint64 len uint64
} }
func (d *digest) Reset() { func (d *digest) Reset() {
d.h[0] = _Init0 d.h[0] = init0
d.h[1] = _Init1 d.h[1] = init1
d.h[2] = _Init2 d.h[2] = init2
d.h[3] = _Init3 d.h[3] = init3
d.h[4] = _Init4 d.h[4] = init4
d.nx = 0 d.nx = 0
d.len = 0 d.len = 0
} }
@ -63,21 +63,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
d.len += uint64(nn) d.len += uint64(nn)
if d.nx > 0 { if d.nx > 0 {
n := len(p) n := len(p)
if n > _Chunk-d.nx { if n > chunk-d.nx {
n = _Chunk - d.nx n = chunk - d.nx
} }
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
d.x[d.nx+i] = p[i] d.x[d.nx+i] = p[i]
} }
d.nx += n d.nx += n
if d.nx == _Chunk { if d.nx == chunk {
_Block(d, d.x[0:]) block(d, d.x[0:])
d.nx = 0 d.nx = 0
} }
p = p[n:] p = p[n:]
} }
n := _Block(d, p) if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
block(d, p[:n])
p = p[n:] p = p[n:]
}
if len(p) > 0 { if len(p) > 0 {
d.nx = copy(d.x[:], p) d.nx = copy(d.x[:], p)
} }

View File

@ -79,3 +79,28 @@ func ExampleNew() {
fmt.Printf("% x", h.Sum(nil)) 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 // Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd
} }
var bench = sha1.New()
var buf = makeBuf()
func makeBuf() []byte {
b := make([]byte, 8<<10)
for i := range b {
b[i] = byte(i)
}
return b
}
func BenchmarkHash1K(b *testing.B) {
b.SetBytes(1024)
for i := 0; i < b.N; i++ {
bench.Write(buf[:1024])
}
}
func BenchmarkHash8K(b *testing.B) {
b.SetBytes(int64(len(buf)))
for i := 0; i < b.N; i++ {
bench.Write(buf)
}
}

View File

@ -15,12 +15,11 @@ const (
_K3 = 0xCA62C1D6 _K3 = 0xCA62C1D6
) )
func _Block(dig *digest, p []byte) int { func block(dig *digest, p []byte) {
var w [80]uint32 var w [80]uint32
n := 0
h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]
for len(p) >= _Chunk { for len(p) >= chunk {
// Can interlace the computation of w with the // Can interlace the computation of w with the
// rounds below if needed for speed. // rounds below if needed for speed.
for i := 0; i < 16; i++ { for i := 0; i < 16; i++ {
@ -72,10 +71,8 @@ func _Block(dig *digest, p []byte) int {
h3 += d h3 += d
h4 += e h4 += e
p = p[_Chunk:] p = p[chunk:]
n += _Chunk
} }
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4 dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4
return n
} }

View File

@ -26,29 +26,29 @@ const Size224 = 28
const BlockSize = 64 const BlockSize = 64
const ( const (
_Chunk = 64 chunk = 64
_Init0 = 0x6A09E667 init0 = 0x6A09E667
_Init1 = 0xBB67AE85 init1 = 0xBB67AE85
_Init2 = 0x3C6EF372 init2 = 0x3C6EF372
_Init3 = 0xA54FF53A init3 = 0xA54FF53A
_Init4 = 0x510E527F init4 = 0x510E527F
_Init5 = 0x9B05688C init5 = 0x9B05688C
_Init6 = 0x1F83D9AB init6 = 0x1F83D9AB
_Init7 = 0x5BE0CD19 init7 = 0x5BE0CD19
_Init0_224 = 0xC1059ED8 init0_224 = 0xC1059ED8
_Init1_224 = 0x367CD507 init1_224 = 0x367CD507
_Init2_224 = 0x3070DD17 init2_224 = 0x3070DD17
_Init3_224 = 0xF70E5939 init3_224 = 0xF70E5939
_Init4_224 = 0xFFC00B31 init4_224 = 0xFFC00B31
_Init5_224 = 0x68581511 init5_224 = 0x68581511
_Init6_224 = 0x64F98FA7 init6_224 = 0x64F98FA7
_Init7_224 = 0xBEFA4FA4 init7_224 = 0xBEFA4FA4
) )
// digest represents the partial evaluation of a checksum. // digest represents the partial evaluation of a checksum.
type digest struct { type digest struct {
h [8]uint32 h [8]uint32
x [_Chunk]byte x [chunk]byte
nx int nx int
len uint64 len uint64
is224 bool // mark if this digest is SHA-224 is224 bool // mark if this digest is SHA-224
@ -56,23 +56,23 @@ type digest struct {
func (d *digest) Reset() { func (d *digest) Reset() {
if !d.is224 { if !d.is224 {
d.h[0] = _Init0 d.h[0] = init0
d.h[1] = _Init1 d.h[1] = init1
d.h[2] = _Init2 d.h[2] = init2
d.h[3] = _Init3 d.h[3] = init3
d.h[4] = _Init4 d.h[4] = init4
d.h[5] = _Init5 d.h[5] = init5
d.h[6] = _Init6 d.h[6] = init6
d.h[7] = _Init7 d.h[7] = init7
} else { } else {
d.h[0] = _Init0_224 d.h[0] = init0_224
d.h[1] = _Init1_224 d.h[1] = init1_224
d.h[2] = _Init2_224 d.h[2] = init2_224
d.h[3] = _Init3_224 d.h[3] = init3_224
d.h[4] = _Init4_224 d.h[4] = init4_224
d.h[5] = _Init5_224 d.h[5] = init5_224
d.h[6] = _Init6_224 d.h[6] = init6_224
d.h[7] = _Init7_224 d.h[7] = init7_224
} }
d.nx = 0 d.nx = 0
d.len = 0 d.len = 0
@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
d.len += uint64(nn) d.len += uint64(nn)
if d.nx > 0 { if d.nx > 0 {
n := len(p) n := len(p)
if n > _Chunk-d.nx { if n > chunk-d.nx {
n = _Chunk - d.nx n = chunk - d.nx
} }
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
d.x[d.nx+i] = p[i] d.x[d.nx+i] = p[i]
} }
d.nx += n d.nx += n
if d.nx == _Chunk { if d.nx == chunk {
_Block(d, d.x[0:]) block(d, d.x[0:])
d.nx = 0 d.nx = 0
} }
p = p[n:] p = p[n:]
} }
n := _Block(d, p) if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
block(d, p[:n])
p = p[n:] p = p[n:]
}
if len(p) > 0 { if len(p) > 0 {
d.nx = copy(d.x[:], p) d.nx = copy(d.x[:], p)
} }

View File

@ -123,3 +123,28 @@ func TestGolden(t *testing.T) {
} }
} }
} }
var bench = New()
var buf = makeBuf()
func makeBuf() []byte {
b := make([]byte, 8<<10)
for i := range b {
b[i] = byte(i)
}
return b
}
func BenchmarkHash1K(b *testing.B) {
b.SetBytes(1024)
for i := 0; i < b.N; i++ {
bench.Write(buf[:1024])
}
}
func BenchmarkHash8K(b *testing.B) {
b.SetBytes(int64(len(buf)))
for i := 0; i < b.N; i++ {
bench.Write(buf)
}
}

View File

@ -75,11 +75,10 @@ var _K = []uint32{
0xc67178f2, 0xc67178f2,
} }
func _Block(dig *digest, p []byte) int { func block(dig *digest, p []byte) {
var w [64]uint32 var w [64]uint32
n := 0
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
for len(p) >= _Chunk { for len(p) >= chunk {
// Can interlace the computation of w with the // Can interlace the computation of w with the
// rounds below if needed for speed. // rounds below if needed for speed.
for i := 0; i < 16; i++ { for i := 0; i < 16; i++ {
@ -87,10 +86,10 @@ func _Block(dig *digest, p []byte) int {
w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
} }
for i := 16; i < 64; i++ { for i := 16; i < 64; i++ {
t1 := (w[i-2]>>17 | w[i-2]<<(32-17)) ^ (w[i-2]>>19 | w[i-2]<<(32-19)) ^ (w[i-2] >> 10) v1 := w[i-2]
t1 := (v1>>17 | v1<<(32-17)) ^ (v1>>19 | v1<<(32-19)) ^ (v1 >> 10)
t2 := (w[i-15]>>7 | w[i-15]<<(32-7)) ^ (w[i-15]>>18 | w[i-15]<<(32-18)) ^ (w[i-15] >> 3) v2 := w[i-15]
t2 := (v2>>7 | v2<<(32-7)) ^ (v2>>18 | v2<<(32-18)) ^ (v2 >> 3)
w[i] = t1 + w[i-7] + t2 + w[i-16] w[i] = t1 + w[i-7] + t2 + w[i-16]
} }
@ -120,10 +119,8 @@ func _Block(dig *digest, p []byte) int {
h6 += g h6 += g
h7 += h h7 += h
p = p[_Chunk:] p = p[chunk:]
n += _Chunk
} }
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
return n
} }

View File

@ -26,29 +26,29 @@ const Size384 = 48
const BlockSize = 128 const BlockSize = 128
const ( const (
_Chunk = 128 chunk = 128
_Init0 = 0x6a09e667f3bcc908 init0 = 0x6a09e667f3bcc908
_Init1 = 0xbb67ae8584caa73b init1 = 0xbb67ae8584caa73b
_Init2 = 0x3c6ef372fe94f82b init2 = 0x3c6ef372fe94f82b
_Init3 = 0xa54ff53a5f1d36f1 init3 = 0xa54ff53a5f1d36f1
_Init4 = 0x510e527fade682d1 init4 = 0x510e527fade682d1
_Init5 = 0x9b05688c2b3e6c1f init5 = 0x9b05688c2b3e6c1f
_Init6 = 0x1f83d9abfb41bd6b init6 = 0x1f83d9abfb41bd6b
_Init7 = 0x5be0cd19137e2179 init7 = 0x5be0cd19137e2179
_Init0_384 = 0xcbbb9d5dc1059ed8 init0_384 = 0xcbbb9d5dc1059ed8
_Init1_384 = 0x629a292a367cd507 init1_384 = 0x629a292a367cd507
_Init2_384 = 0x9159015a3070dd17 init2_384 = 0x9159015a3070dd17
_Init3_384 = 0x152fecd8f70e5939 init3_384 = 0x152fecd8f70e5939
_Init4_384 = 0x67332667ffc00b31 init4_384 = 0x67332667ffc00b31
_Init5_384 = 0x8eb44a8768581511 init5_384 = 0x8eb44a8768581511
_Init6_384 = 0xdb0c2e0d64f98fa7 init6_384 = 0xdb0c2e0d64f98fa7
_Init7_384 = 0x47b5481dbefa4fa4 init7_384 = 0x47b5481dbefa4fa4
) )
// digest represents the partial evaluation of a checksum. // digest represents the partial evaluation of a checksum.
type digest struct { type digest struct {
h [8]uint64 h [8]uint64
x [_Chunk]byte x [chunk]byte
nx int nx int
len uint64 len uint64
is384 bool // mark if this digest is SHA-384 is384 bool // mark if this digest is SHA-384
@ -56,23 +56,23 @@ type digest struct {
func (d *digest) Reset() { func (d *digest) Reset() {
if !d.is384 { if !d.is384 {
d.h[0] = _Init0 d.h[0] = init0
d.h[1] = _Init1 d.h[1] = init1
d.h[2] = _Init2 d.h[2] = init2
d.h[3] = _Init3 d.h[3] = init3
d.h[4] = _Init4 d.h[4] = init4
d.h[5] = _Init5 d.h[5] = init5
d.h[6] = _Init6 d.h[6] = init6
d.h[7] = _Init7 d.h[7] = init7
} else { } else {
d.h[0] = _Init0_384 d.h[0] = init0_384
d.h[1] = _Init1_384 d.h[1] = init1_384
d.h[2] = _Init2_384 d.h[2] = init2_384
d.h[3] = _Init3_384 d.h[3] = init3_384
d.h[4] = _Init4_384 d.h[4] = init4_384
d.h[5] = _Init5_384 d.h[5] = init5_384
d.h[6] = _Init6_384 d.h[6] = init6_384
d.h[7] = _Init7_384 d.h[7] = init7_384
} }
d.nx = 0 d.nx = 0
d.len = 0 d.len = 0
@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
d.len += uint64(nn) d.len += uint64(nn)
if d.nx > 0 { if d.nx > 0 {
n := len(p) n := len(p)
if n > _Chunk-d.nx { if n > chunk-d.nx {
n = _Chunk - d.nx n = chunk - d.nx
} }
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
d.x[d.nx+i] = p[i] d.x[d.nx+i] = p[i]
} }
d.nx += n d.nx += n
if d.nx == _Chunk { if d.nx == chunk {
_Block(d, d.x[0:]) block(d, d.x[0:])
d.nx = 0 d.nx = 0
} }
p = p[n:] p = p[n:]
} }
n := _Block(d, p) if len(p) >= chunk {
n := len(p) &^ (chunk - 1)
block(d, p[:n])
p = p[n:] p = p[n:]
}
if len(p) > 0 { if len(p) > 0 {
d.nx = copy(d.x[:], p) d.nx = copy(d.x[:], p)
} }

View File

@ -123,3 +123,28 @@ func TestGolden(t *testing.T) {
} }
} }
} }
var bench = New()
var buf = makeBuf()
func makeBuf() []byte {
b := make([]byte, 8<<10)
for i := range b {
b[i] = byte(i)
}
return b
}
func BenchmarkHash1K(b *testing.B) {
b.SetBytes(1024)
for i := 0; i < b.N; i++ {
bench.Write(buf[:1024])
}
}
func BenchmarkHash8K(b *testing.B) {
b.SetBytes(int64(len(buf)))
for i := 0; i < b.N; i++ {
bench.Write(buf)
}
}

View File

@ -91,20 +91,20 @@ var _K = []uint64{
0x6c44198c4a475817, 0x6c44198c4a475817,
} }
func _Block(dig *digest, p []byte) int { func block(dig *digest, p []byte) {
var w [80]uint64 var w [80]uint64
n := 0
h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] h0, h1, h2, h3, h4, h5, h6, h7 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7]
for len(p) >= _Chunk { for len(p) >= chunk {
for i := 0; i < 16; i++ { for i := 0; i < 16; i++ {
j := i * 8 j := i * 8
w[i] = uint64(p[j])<<56 | uint64(p[j+1])<<48 | uint64(p[j+2])<<40 | uint64(p[j+3])<<32 | w[i] = uint64(p[j])<<56 | uint64(p[j+1])<<48 | uint64(p[j+2])<<40 | uint64(p[j+3])<<32 |
uint64(p[j+4])<<24 | uint64(p[j+5])<<16 | uint64(p[j+6])<<8 | uint64(p[j+7]) uint64(p[j+4])<<24 | uint64(p[j+5])<<16 | uint64(p[j+6])<<8 | uint64(p[j+7])
} }
for i := 16; i < 80; i++ { for i := 16; i < 80; i++ {
t1 := (w[i-2]>>19 | w[i-2]<<(64-19)) ^ (w[i-2]>>61 | w[i-2]<<(64-61)) ^ (w[i-2] >> 6) v1 := w[i-2]
t1 := (v1>>19 | v1<<(64-19)) ^ (v1>>61 | v1<<(64-61)) ^ (v1 >> 6)
t2 := (w[i-15]>>1 | w[i-15]<<(64-1)) ^ (w[i-15]>>8 | w[i-15]<<(64-8)) ^ (w[i-15] >> 7) v2 := w[i-15]
t2 := (v2>>1 | v2<<(64-1)) ^ (v2>>8 | v2<<(64-8)) ^ (v2 >> 7)
w[i] = t1 + w[i-7] + t2 + w[i-16] w[i] = t1 + w[i-7] + t2 + w[i-16]
} }
@ -135,10 +135,8 @@ func _Block(dig *digest, p []byte) int {
h6 += g h6 += g
h7 += h h7 += h
p = p[_Chunk:] p = p[chunk:]
n += _Chunk
} }
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7 dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4], dig.h[5], dig.h[6], dig.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
return n
} }

View File

@ -55,9 +55,11 @@ var cipherSuites = []*cipherSuite{
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1}, {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1},
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1}, {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1},
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1}, {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1},
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, false, cipherAES, macSHA1},
{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, true, cipherRC4, macSHA1},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1}, {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1},
{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, true, cipherAES, macSHA1},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
} }
func cipherRC4(key, iv []byte, isRead bool) interface{} { func cipherRC4(key, iv []byte, isRead bool) interface{} {
@ -182,7 +184,9 @@ 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_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 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_128_CBC_SHA uint16 = 0xc013
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
) )

View File

@ -41,6 +41,7 @@ const (
const ( const (
typeClientHello uint8 = 1 typeClientHello uint8 = 1
typeServerHello uint8 = 2 typeServerHello uint8 = 2
typeNewSessionTicket uint8 = 4
typeCertificate uint8 = 11 typeCertificate uint8 = 11
typeServerKeyExchange uint8 = 12 typeServerKeyExchange uint8 = 12
typeCertificateRequest uint8 = 13 typeCertificateRequest uint8 = 13
@ -63,6 +64,7 @@ var (
extensionStatusRequest uint16 = 5 extensionStatusRequest uint16 = 5
extensionSupportedCurves uint16 = 10 extensionSupportedCurves uint16 = 10
extensionSupportedPoints uint16 = 11 extensionSupportedPoints uint16 = 11
extensionSessionTicket uint16 = 35
extensionNextProtoNeg uint16 = 13172 // not IANA assigned extensionNextProtoNeg uint16 = 13172 // not IANA assigned
) )
@ -97,6 +99,7 @@ const (
// 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
DidResume bool
CipherSuite uint16 CipherSuite uint16
NegotiatedProtocol string NegotiatedProtocol string
NegotiatedProtocolIsMutual bool NegotiatedProtocolIsMutual bool
@ -180,6 +183,22 @@ type Config struct {
// CipherSuites is a list of supported cipher suites. If CipherSuites // CipherSuites is a list of supported cipher suites. If CipherSuites
// is nil, TLS uses a list of suites supported by the implementation. // is nil, TLS uses a list of suites supported by the implementation.
CipherSuites []uint16 CipherSuites []uint16
// SessionTicketsDisabled may be set to true to disable session ticket
// (resumption) support.
SessionTicketsDisabled bool
// SessionTicketKey is used by TLS servers to provide session
// resumption. See RFC 5077. If zero, it will be filled with
// random data before the first server handshake.
//
// If multiple servers are terminating connections for the same host
// they should all have the same SessionTicketKey. If the
// SessionTicketKey leaks, previously recorded and future TLS
// connections using that key are compromised.
SessionTicketKey [32]byte
serverInitOnce sync.Once
} }
func (c *Config) rand() io.Reader { func (c *Config) rand() io.Reader {

View File

@ -31,6 +31,7 @@ type Conn struct {
haveVers bool // version has been negotiated haveVers bool // version has been negotiated
config *Config // configuration passed to constructor config *Config // configuration passed to constructor
handshakeComplete bool handshakeComplete bool
didResume bool // whether this connection was a session resumption
cipherSuite uint16 cipherSuite uint16
ocspResponse []byte // stapled OCSP response ocspResponse []byte // stapled OCSP response
peerCertificates []*x509.Certificate peerCertificates []*x509.Certificate
@ -44,8 +45,7 @@ type Conn struct {
clientProtocolFallback bool clientProtocolFallback bool
// first permanent error // first permanent error
errMutex sync.Mutex connErr
err error
// input/output // input/output
in, out halfConn // in.Mutex < out.Mutex in, out halfConn // in.Mutex < out.Mutex
@ -56,21 +56,25 @@ type Conn struct {
tmp [16]byte tmp [16]byte
} }
func (c *Conn) setError(err error) error { type connErr struct {
c.errMutex.Lock() mu sync.Mutex
defer c.errMutex.Unlock() value error
}
if c.err == nil { func (e *connErr) setError(err error) error {
c.err = err e.mu.Lock()
defer e.mu.Unlock()
if e.value == nil {
e.value = err
} }
return err return err
} }
func (c *Conn) error() error { func (e *connErr) error() error {
c.errMutex.Lock() e.mu.Lock()
defer c.errMutex.Unlock() defer e.mu.Unlock()
return e.value
return c.err
} }
// Access to net.Conn methods. // Access to net.Conn methods.
@ -660,8 +664,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
c.tmp[0] = alertLevelError c.tmp[0] = alertLevelError
c.tmp[1] = byte(err.(alert)) c.tmp[1] = byte(err.(alert))
c.writeRecord(recordTypeAlert, c.tmp[0:2]) c.writeRecord(recordTypeAlert, c.tmp[0:2])
c.err = &net.OpError{Op: "local error", Err: err} return n, c.setError(&net.OpError{Op: "local error", Err: err})
return n, c.err
} }
} }
return return
@ -672,8 +675,8 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
// c.in.Mutex < L; c.out.Mutex < L. // c.in.Mutex < L; c.out.Mutex < L.
func (c *Conn) readHandshake() (interface{}, error) { func (c *Conn) readHandshake() (interface{}, error) {
for c.hand.Len() < 4 { for c.hand.Len() < 4 {
if c.err != nil { if err := c.error(); err != nil {
return nil, c.err return nil, err
} }
if err := c.readRecord(recordTypeHandshake); err != nil { if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err return nil, err
@ -684,11 +687,11 @@ func (c *Conn) readHandshake() (interface{}, error) {
n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
if n > maxHandshake { if n > maxHandshake {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return nil, c.err return nil, c.error()
} }
for c.hand.Len() < 4+n { for c.hand.Len() < 4+n {
if c.err != nil { if err := c.error(); err != nil {
return nil, c.err return nil, err
} }
if err := c.readRecord(recordTypeHandshake); err != nil { if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err return nil, err
@ -738,12 +741,12 @@ func (c *Conn) readHandshake() (interface{}, error) {
// Write writes data to the connection. // Write writes data to the connection.
func (c *Conn) Write(b []byte) (int, error) { func (c *Conn) Write(b []byte) (int, error) {
if c.err != nil { if err := c.error(); err != nil {
return 0, c.err return 0, err
} }
if c.err = c.Handshake(); c.err != nil { if err := c.Handshake(); err != nil {
return 0, c.err return 0, c.setError(err)
} }
c.out.Lock() c.out.Lock()
@ -753,9 +756,8 @@ func (c *Conn) Write(b []byte) (int, error) {
return 0, alertInternalError return 0, alertInternalError
} }
var n int n, err := c.writeRecord(recordTypeApplicationData, b)
n, c.err = c.writeRecord(recordTypeApplicationData, b) return n, c.setError(err)
return n, c.err
} }
// Read can be made to time out and return a net.Error with Timeout() == true // Read can be made to time out and return a net.Error with Timeout() == true
@ -768,14 +770,14 @@ 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.err == nil { for c.input == nil && c.error() == nil {
if err := c.readRecord(recordTypeApplicationData); err != nil { if err := c.readRecord(recordTypeApplicationData); err != nil {
// Soft error, like EAGAIN // Soft error, like EAGAIN
return 0, err return 0, err
} }
} }
if c.err != nil { if err := c.error(); err != nil {
return 0, c.err return 0, err
} }
n, err = c.input.Read(b) n, err = c.input.Read(b)
if c.input.off >= len(c.input.data) { if c.input.off >= len(c.input.data) {
@ -829,6 +831,7 @@ func (c *Conn) ConnectionState() ConnectionState {
state.HandshakeComplete = c.handshakeComplete state.HandshakeComplete = c.handshakeComplete
if c.handshakeComplete { if c.handshakeComplete {
state.NegotiatedProtocol = c.clientProtocol state.NegotiatedProtocol = c.clientProtocol
state.DidResume = c.didResume
state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
state.CipherSuite = c.cipherSuite state.CipherSuite = c.cipherSuite
state.PeerCertificates = c.peerCertificates state.PeerCertificates = c.peerCertificates

View File

@ -278,8 +278,9 @@ func (c *Conn) clientHandshake() error {
c.writeRecord(recordTypeHandshake, certVerify.marshal()) c.writeRecord(recordTypeHandshake, certVerify.marshal())
} }
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := masterSecret := masterFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random)
keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromMasterSecret(c.vers, masterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */) clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */)
clientHash := suite.mac(c.vers, clientMAC) clientHash := suite.mac(c.vers, clientMAC)
@ -306,8 +307,8 @@ func (c *Conn) clientHandshake() error {
serverHash := suite.mac(c.vers, serverMAC) serverHash := suite.mac(c.vers, serverMAC)
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.readRecord(recordTypeChangeCipherSpec) c.readRecord(recordTypeChangeCipherSpec)
if c.err != nil { if err := c.error(); err != nil {
return c.err return err
} }
msg, err = c.readHandshake() msg, err = c.readHandshake()

View File

@ -9,6 +9,7 @@ import (
"flag" "flag"
"io" "io"
"net" "net"
"os"
"testing" "testing"
) )
@ -39,7 +40,15 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config *
} }
func TestHandshakeClientRC4(t *testing.T) { func TestHandshakeClientRC4(t *testing.T) {
testClientScript(t, "RC4", rc4ClientScript, testConfig) var config = *testConfig
config.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA}
testClientScript(t, "RC4", rc4ClientScript, &config)
}
func TestHandshakeClientECDHEAES(t *testing.T) {
var config = *testConfig
config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}
testClientScript(t, "ECDHE-AES", ecdheAESClientScript, &config)
} }
var connect = flag.Bool("connect", false, "connect to a TLS server on :10443") var connect = flag.Bool("connect", false, "connect to a TLS server on :10443")
@ -49,25 +58,33 @@ func TestRunClient(t *testing.T) {
return return
} }
testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA} tcpConn, err := net.Dial("tcp", "127.0.0.1:10443")
conn, err := Dial("tcp", "127.0.0.1:10443", testConfig)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
record := &recordingConn{
Conn: tcpConn,
}
config := GetTestConfig()
conn := Client(record, config)
if err := conn.Handshake(); err != nil {
t.Fatalf("error from TLS handshake: %s", err)
}
conn.Write([]byte("hello\n")) conn.Write([]byte("hello\n"))
conn.Close() conn.Close()
record.WriteTo(os.Stdout)
} }
// Script of interaction with gnutls implementation. // Script of interaction with gnutls implementation.
// The values for this test are obtained by building and running in client mode: // The values for this test are obtained by building and running in client mode:
// % go test -run "TestRunClient" -connect // % go test -test.run "TestRunClient" -connect
// and then: // The recorded bytes are written to stdout.
// % gnutls-serv -p 10443 --debug 100 --x509keyfile key.pem --x509certfile cert.pem -a > /tmp/log 2>&1
// % python parse-gnutls-cli-debug-log.py < /tmp/log
// //
// Where key.pem is: // The server private key is:
// -----BEGIN RSA PRIVATE KEY----- // -----BEGIN RSA PRIVATE KEY-----
// MIIBPAIBAAJBAJ+zw4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVD // MIIBPAIBAAJBAJ+zw4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVD
// TGiXav6ooKXfX3j/7tdkuD8Ey2//Kv7+ue0CAwEAAQJAN6W31vDEP2DjdqhzCDDu // TGiXav6ooKXfX3j/7tdkuD8Ey2//Kv7+ue0CAwEAAQJAN6W31vDEP2DjdqhzCDDu
@ -78,17 +95,20 @@ func TestRunClient(t *testing.T) {
// vnlEGo8K85u+KwIOimM48ZG8oTk7iFdkqLJR1utT3aU= // vnlEGo8K85u+KwIOimM48ZG8oTk7iFdkqLJR1utT3aU=
// -----END RSA PRIVATE KEY----- // -----END RSA PRIVATE KEY-----
// //
// and cert.pem is: // and certificate is:
// -----BEGIN CERTIFICATE----- // -----BEGIN CERTIFICATE-----
// MIIBoDCCAUoCAQAwDQYJKoZIhvcNAQEEBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV // MIICKzCCAdWgAwIBAgIJALE1E2URIMWSMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
// BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD // BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
// VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw05NzA5MDkwMzQxMjZa // aWRnaXRzIFB0eSBMdGQwHhcNMTIwNDA2MTcxMDEzWhcNMTUwNDA2MTcxMDEzWjBF
// Fw05NzEwMDkwMzQxMjZaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0 // MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
// YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNVBAMT // ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ+z
// DkVyaWMgdGhlIFlvdW5nMFEwCQYFKw4DAgwFAANEAAJBALVEqPODnpI4rShlY8S7 // w4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVDTGiXav6ooKXfX3j/
// tB713JNvabvn6Gned7zylwLLiXQAo/PAT6mfdWPTyCX9RlId/Aroh1ou893BA32Q // 7tdkuD8Ey2//Kv7+ue0CAwEAAaOBpzCBpDAdBgNVHQ4EFgQUeKaXmmO1xaGlM7oi
// sggwDQYJKoZIhvcNAQEEBQADQQCU5SSgapJSdRXJoX+CpCvFy+JVh9HpSjCpSNKO // fCNuWxt6zCswdQYDVR0jBG4wbIAUeKaXmmO1xaGlM7oifCNuWxt6zCuhSaRHMEUx
// 19raHv98hKAUJuP9HyM+SUsffO6mAIgitUaqW8/wDMePhEC3 // CzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl
// cm5ldCBXaWRnaXRzIFB0eSBMdGSCCQCxNRNlESDFkjAMBgNVHRMEBTADAQH/MA0G
// CSqGSIb3DQEBBQUAA0EAhTZAc8G7GtrUWZ8tonAxRnTsg26oyDxRrzms7EC86CJG
// HZnWRiok1IsFCEv7NRFukrt3uuQSu/TIXpyBqJdgTA==
// -----END CERTIFICATE----- // -----END CERTIFICATE-----
var rc4ClientScript = [][]byte{ var rc4ClientScript = [][]byte{
{ {
@ -210,3 +230,163 @@ var rc4ClientScript = [][]byte{
0x59, 0xac, 0xc6, 0xb5, 0x56, 0x55, 0x96, 0x59, 0xac, 0xc6, 0xb5, 0x56, 0x55, 0x96,
}, },
} }
var ecdheAESClientScript = [][]byte{
{
0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x13,
0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00,
0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00,
0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
},
{
0x16, 0x03, 0x01, 0x00, 0x54, 0x02, 0x00, 0x00,
0x50, 0x03, 0x01, 0x4f, 0x7f, 0x24, 0x25, 0x10,
0xa8, 0x9d, 0xb1, 0x33, 0xd6, 0x53, 0x81, 0xce,
0xb0, 0x69, 0xed, 0x1b, 0x9c, 0x5e, 0x40, 0x3a,
0x4d, 0x06, 0xbc, 0xc7, 0x84, 0x51, 0x5a, 0x30,
0x40, 0x50, 0x48, 0x20, 0xcd, 0x91, 0x80, 0x08,
0xff, 0x82, 0x38, 0xc6, 0x03, 0x2d, 0x45, 0x4c,
0x91, 0xbb, 0xcc, 0x27, 0x3d, 0x58, 0xff, 0x0d,
0x26, 0x34, 0x7b, 0x48, 0x7a, 0xce, 0x25, 0x20,
0x90, 0x0f, 0x35, 0x9f, 0xc0, 0x13, 0x00, 0x00,
0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
0x02, 0x16, 0x03, 0x01, 0x02, 0x39, 0x0b, 0x00,
0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f,
0x30, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5,
0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00,
0xb1, 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92,
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e,
0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x30, 0x36,
0x31, 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17,
0x0d, 0x31, 0x35, 0x30, 0x34, 0x30, 0x36, 0x31,
0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45,
0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61,
0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74,
0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69,
0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b,
0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3,
0xc3, 0x84, 0x27, 0x95, 0xff, 0x12, 0x31, 0x52,
0x0f, 0x15, 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80,
0xe6, 0x36, 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61,
0x8d, 0xe0, 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe,
0x55, 0x66, 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a,
0xfe, 0xa8, 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff,
0xee, 0xd7, 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f,
0xff, 0x2a, 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03,
0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
0x04, 0x16, 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a,
0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22,
0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b,
0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
0x6e, 0x30, 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97,
0x9a, 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba,
0x22, 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc,
0x2b, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1,
0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30,
0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x85,
0x36, 0x40, 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4,
0x59, 0x9f, 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74,
0xec, 0x83, 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf,
0x39, 0xac, 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46,
0x1d, 0x99, 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b,
0x05, 0x08, 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92,
0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8,
0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16,
0x03, 0x01, 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87,
0x03, 0x00, 0x17, 0x41, 0x04, 0x0b, 0xe5, 0x39,
0xde, 0x17, 0x7a, 0xaf, 0x96, 0xd5, 0x16, 0x01,
0xa8, 0x06, 0x80, 0x98, 0x75, 0x52, 0x56, 0x92,
0x15, 0xf9, 0x8d, 0xc0, 0x98, 0x62, 0xed, 0x54,
0xb7, 0xef, 0x03, 0x11, 0x34, 0x82, 0x65, 0xd1,
0xde, 0x25, 0x15, 0x4c, 0xf3, 0xdf, 0x4d, 0xbd,
0x6c, 0xed, 0x3d, 0xd6, 0x04, 0xcc, 0xd1, 0xf7,
0x6d, 0x32, 0xb1, 0x1c, 0x59, 0xca, 0xfb, 0xbc,
0x61, 0xeb, 0x4b, 0xe6, 0x00, 0x00, 0x40, 0x3e,
0xe6, 0x23, 0x54, 0x61, 0x3f, 0x63, 0x16, 0xeb,
0x5c, 0xc3, 0xba, 0x8a, 0x19, 0x13, 0x60, 0x9f,
0x23, 0xbf, 0x36, 0x1a, 0x32, 0x7a, 0xae, 0x34,
0x7f, 0x2f, 0x89, 0x85, 0xe1, 0x0e, 0x93, 0xd7,
0xf0, 0xab, 0xa1, 0x0d, 0x54, 0x95, 0x79, 0x0b,
0xb4, 0xf1, 0x1c, 0x1d, 0x0f, 0x8c, 0x16, 0xec,
0x82, 0x60, 0xee, 0xa3, 0x71, 0x2f, 0xaf, 0x3e,
0xf1, 0xbd, 0xb5, 0x1b, 0x7f, 0xe0, 0xd2, 0x16,
0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
},
{
0x16, 0x03, 0x01, 0x00, 0x46, 0x10, 0x00, 0x00,
0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d,
0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5,
0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd,
0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce,
0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e,
0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56,
0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49,
0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b,
0xdc, 0x5a, 0x89, 0x14, 0x03, 0x01, 0x00, 0x01,
0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x09, 0xac,
0xbe, 0x94, 0x75, 0x4d, 0x73, 0x45, 0xbd, 0xa8,
0x0c, 0xe3, 0x5f, 0x72, 0x0b, 0x40, 0x4f, 0xd0,
0xd2, 0xcb, 0x16, 0x50, 0xfe, 0xdd, 0x1a, 0x33,
0x5c, 0x18, 0x37, 0x98, 0x42, 0xfc, 0x25, 0x42,
0x33, 0xce, 0x60, 0xcf, 0x8e, 0x95, 0x6e, 0x48,
0xed, 0x00, 0x35, 0x50, 0x26, 0x7f,
},
{
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
0x01, 0x00, 0x30, 0xf6, 0x6a, 0xdb, 0x83, 0xd4,
0x3c, 0x77, 0x52, 0xad, 0xc0, 0x0f, 0x3a, 0x2c,
0x42, 0xb9, 0x60, 0x4b, 0xb2, 0xf6, 0x84, 0xfd,
0x4e, 0x96, 0xfc, 0x15, 0xe7, 0x94, 0x25, 0xb0,
0x59, 0x9f, 0xdd, 0xb6, 0x58, 0x03, 0x13, 0x8d,
0xeb, 0xb0, 0xad, 0x30, 0x31, 0x58, 0x6c, 0xa0,
0x8f, 0x57, 0x50,
},
{
0x17, 0x03, 0x01, 0x00, 0x20, 0xab, 0x64, 0x3d,
0x79, 0x69, 0x3e, 0xba, 0xc4, 0x24, 0x7b, 0xe5,
0xe5, 0x23, 0x66, 0x6f, 0x32, 0xdf, 0x50, 0x7c,
0x06, 0x2a, 0x02, 0x82, 0x79, 0x40, 0xdb, 0xb1,
0x04, 0xc0, 0x2b, 0xdc, 0x3a, 0x15, 0x03, 0x01,
0x00, 0x20, 0xf8, 0xad, 0xca, 0xd7, 0x96, 0xf0,
0xd6, 0xa3, 0x62, 0xe1, 0x03, 0x44, 0xdb, 0xd0,
0xc9, 0x63, 0x3e, 0x1b, 0x70, 0x41, 0x57, 0x0c,
0xd8, 0x8e, 0x71, 0x49, 0x68, 0xe3, 0x04, 0x53,
0x5a, 0xbe,
},
}

View File

@ -18,6 +18,8 @@ type clientHelloMsg struct {
ocspStapling bool ocspStapling bool
supportedCurves []uint16 supportedCurves []uint16
supportedPoints []uint8 supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
} }
func (m *clientHelloMsg) equal(i interface{}) bool { func (m *clientHelloMsg) equal(i interface{}) bool {
@ -36,7 +38,9 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
m.serverName == m1.serverName && m.serverName == m1.serverName &&
m.ocspStapling == m1.ocspStapling && m.ocspStapling == m1.ocspStapling &&
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 &&
bytes.Equal(m.sessionTicket, m1.sessionTicket)
} }
func (m *clientHelloMsg) marshal() []byte { func (m *clientHelloMsg) marshal() []byte {
@ -66,6 +70,10 @@ func (m *clientHelloMsg) marshal() []byte {
extensionsLength += 1 + len(m.supportedPoints) extensionsLength += 1 + len(m.supportedPoints)
numExtensions++ numExtensions++
} }
if m.ticketSupported {
extensionsLength += len(m.sessionTicket)
numExtensions++
}
if numExtensions > 0 { if numExtensions > 0 {
extensionsLength += 4 * numExtensions extensionsLength += 4 * numExtensions
length += 2 + extensionsLength length += 2 + extensionsLength
@ -180,6 +188,17 @@ func (m *clientHelloMsg) marshal() []byte {
z = z[1:] z = z[1:]
} }
} }
if m.ticketSupported {
// http://tools.ietf.org/html/rfc5077#section-3.2
z[0] = byte(extensionSessionTicket >> 8)
z[1] = byte(extensionSessionTicket)
l := len(m.sessionTicket)
z[2] = byte(l >> 8)
z[3] = byte(l)
z = z[4:]
copy(z, m.sessionTicket)
z = z[len(m.sessionTicket):]
}
m.raw = x m.raw = x
@ -228,6 +247,8 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
m.nextProtoNeg = false m.nextProtoNeg = false
m.serverName = "" m.serverName = ""
m.ocspStapling = false m.ocspStapling = false
m.ticketSupported = false
m.sessionTicket = nil
if len(data) == 0 { if len(data) == 0 {
// ClientHello is optionally followed by extension data // ClientHello is optionally followed by extension data
@ -311,6 +332,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
} }
m.supportedPoints = make([]uint8, l) m.supportedPoints = make([]uint8, l)
copy(m.supportedPoints, data[1:]) copy(m.supportedPoints, data[1:])
case extensionSessionTicket:
// http://tools.ietf.org/html/rfc5077#section-3.2
m.ticketSupported = true
m.sessionTicket = data[:length]
} }
data = data[length:] data = data[length:]
} }
@ -328,6 +353,7 @@ type serverHelloMsg struct {
nextProtoNeg bool nextProtoNeg bool
nextProtos []string nextProtos []string
ocspStapling bool ocspStapling bool
ticketSupported bool
} }
func (m *serverHelloMsg) equal(i interface{}) bool { func (m *serverHelloMsg) equal(i interface{}) bool {
@ -344,7 +370,8 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
m.compressionMethod == m1.compressionMethod && m.compressionMethod == m1.compressionMethod &&
m.nextProtoNeg == m1.nextProtoNeg && m.nextProtoNeg == m1.nextProtoNeg &&
eqStrings(m.nextProtos, m1.nextProtos) && eqStrings(m.nextProtos, m1.nextProtos) &&
m.ocspStapling == m1.ocspStapling m.ocspStapling == m1.ocspStapling &&
m.ticketSupported == m1.ticketSupported
} }
func (m *serverHelloMsg) marshal() []byte { func (m *serverHelloMsg) marshal() []byte {
@ -368,6 +395,9 @@ func (m *serverHelloMsg) marshal() []byte {
if m.ocspStapling { if m.ocspStapling {
numExtensions++ numExtensions++
} }
if m.ticketSupported {
numExtensions++
}
if numExtensions > 0 { if numExtensions > 0 {
extensionsLength += 4 * numExtensions extensionsLength += 4 * numExtensions
length += 2 + extensionsLength length += 2 + extensionsLength
@ -416,6 +446,11 @@ func (m *serverHelloMsg) marshal() []byte {
z[1] = byte(extensionStatusRequest) z[1] = byte(extensionStatusRequest)
z = z[4:] z = z[4:]
} }
if m.ticketSupported {
z[0] = byte(extensionSessionTicket >> 8)
z[1] = byte(extensionSessionTicket)
z = z[4:]
}
m.raw = x m.raw = x
@ -445,6 +480,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
m.nextProtoNeg = false m.nextProtoNeg = false
m.nextProtos = nil m.nextProtos = nil
m.ocspStapling = false m.ocspStapling = false
m.ticketSupported = false
if len(data) == 0 { if len(data) == 0 {
// ServerHello is optionally followed by extension data // ServerHello is optionally followed by extension data
@ -474,14 +510,14 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
switch extension { switch extension {
case extensionNextProtoNeg: case extensionNextProtoNeg:
m.nextProtoNeg = true m.nextProtoNeg = true
d := data d := data[:length]
for len(d) > 0 { for len(d) > 0 {
l := int(d[0]) l := int(d[0])
d = d[1:] d = d[1:]
if l == 0 || l > len(d) { if l == 0 || l > len(d) {
return false return false
} }
m.nextProtos = append(m.nextProtos, string(d[0:l])) m.nextProtos = append(m.nextProtos, string(d[:l]))
d = d[l:] d = d[l:]
} }
case extensionStatusRequest: case extensionStatusRequest:
@ -489,6 +525,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
return false return false
} }
m.ocspStapling = true m.ocspStapling = true
case extensionSessionTicket:
if length > 0 {
return false
}
m.ticketSupported = true
} }
data = data[length:] data = data[length:]
} }
@ -1030,6 +1071,65 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
return true return true
} }
type newSessionTicketMsg struct {
raw []byte
ticket []byte
}
func (m *newSessionTicketMsg) equal(i interface{}) bool {
m1, ok := i.(*newSessionTicketMsg)
if !ok {
return false
}
return bytes.Equal(m.raw, m1.raw) &&
bytes.Equal(m.ticket, m1.ticket)
}
func (m *newSessionTicketMsg) marshal() (x []byte) {
if m.raw != nil {
return m.raw
}
// See http://tools.ietf.org/html/rfc5077#section-3.3
ticketLen := len(m.ticket)
length := 2 + 4 + ticketLen
x = make([]byte, 4+length)
x[0] = typeNewSessionTicket
x[1] = uint8(length >> 16)
x[2] = uint8(length >> 8)
x[3] = uint8(length)
x[8] = uint8(ticketLen >> 8)
x[9] = uint8(ticketLen)
copy(x[10:], m.ticket)
m.raw = x
return
}
func (m *newSessionTicketMsg) unmarshal(data []byte) bool {
m.raw = data
if len(data) < 10 {
return false
}
length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
if uint32(len(data))-4 != length {
return false
}
ticketLen := int(data[8])<<8 + int(data[9])
if len(data)-10 != ticketLen {
return false
}
m.ticket = data[10:]
return true
}
func eqUint16s(x, y []uint16) bool { func eqUint16s(x, y []uint16) bool {
if len(x) != len(y) { if len(x) != len(y) {
return false return false

View File

@ -22,6 +22,8 @@ var tests = []interface{}{
&certificateStatusMsg{}, &certificateStatusMsg{},
&clientKeyExchangeMsg{}, &clientKeyExchangeMsg{},
&nextProtoMsg{}, &nextProtoMsg{},
&newSessionTicketMsg{},
&sessionState{},
} }
type testMessage interface { type testMessage interface {
@ -127,6 +129,12 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
for i := range m.supportedCurves { for i := range m.supportedCurves {
m.supportedCurves[i] = uint16(rand.Intn(30000)) m.supportedCurves[i] = uint16(rand.Intn(30000))
} }
if rand.Intn(10) > 5 {
m.ticketSupported = true
if rand.Intn(10) > 5 {
m.sessionTicket = randomBytes(rand.Intn(300), rand)
}
}
return reflect.ValueOf(m) return reflect.ValueOf(m)
} }
@ -149,6 +157,13 @@ func (*serverHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
} }
} }
if rand.Intn(10) > 5 {
m.ocspStapling = true
}
if rand.Intn(10) > 5 {
m.ticketSupported = true
}
return reflect.ValueOf(m) return reflect.ValueOf(m)
} }
@ -207,3 +222,22 @@ func (*nextProtoMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m.proto = randomString(rand.Intn(255), rand) m.proto = randomString(rand.Intn(255), rand)
return reflect.ValueOf(m) return reflect.ValueOf(m)
} }
func (*newSessionTicketMsg) Generate(rand *rand.Rand, size int) reflect.Value {
m := &newSessionTicketMsg{}
m.ticket = randomBytes(rand.Intn(4), rand)
return reflect.ValueOf(m)
}
func (*sessionState) Generate(rand *rand.Rand, size int) reflect.Value {
s := &sessionState{}
s.vers = uint16(rand.Intn(10000))
s.cipherSuite = uint16(rand.Intn(10000))
s.masterSecret = randomBytes(rand.Intn(100), rand)
numCerts := rand.Intn(20)
s.certificates = make([][]byte, numCerts)
for i := 0; i < numCerts; i++ {
s.certificates[i] = randomBytes(rand.Intn(10)+1, rand)
}
return reflect.ValueOf(s)
}

View File

@ -13,31 +13,120 @@ import (
"io" "io"
) )
// serverHandshakeState contains details of a server handshake in progress.
// It's discarded once the handshake has completed.
type serverHandshakeState struct {
c *Conn
clientHello *clientHelloMsg
hello *serverHelloMsg
suite *cipherSuite
ellipticOk bool
sessionState *sessionState
finishedHash finishedHash
masterSecret []byte
certsFromClient [][]byte
}
// serverHandshake performs a TLS handshake as a server.
func (c *Conn) serverHandshake() error { func (c *Conn) serverHandshake() error {
config := c.config config := c.config
msg, err := c.readHandshake()
// If this is the first server handshake, we generate a random key to
// encrypt the tickets with.
config.serverInitOnce.Do(func() {
if config.SessionTicketsDisabled {
return
}
// If the key has already been set then we have nothing to do.
for _, b := range config.SessionTicketKey {
if b != 0 {
return
}
}
if _, err := io.ReadFull(config.rand(), config.SessionTicketKey[:]); err != nil {
config.SessionTicketsDisabled = true
}
})
hs := serverHandshakeState{
c: c,
}
isResume, err := hs.readClientHello()
if err != nil { if err != nil {
return err return err
} }
clientHello, ok := msg.(*clientHelloMsg)
if !ok { // For an overview of TLS handshaking, see https://tools.ietf.org/html/rfc5246#section-7.3
return c.sendAlert(alertUnexpectedMessage) if isResume {
// The client has included a session ticket and so we do an abbreviated handshake.
if err := hs.doResumeHandshake(); err != nil {
return err
} }
vers, ok := mutualVersion(clientHello.vers) if err := hs.establishKeys(); err != nil {
if !ok { return err
return c.sendAlert(alertProtocolVersion) }
if err := hs.sendFinished(); err != nil {
return err
}
if err := hs.readFinished(); err != nil {
return err
}
c.didResume = true
} else {
// The client didn't include a session ticket, or it wasn't
// valid so we do a full handshake.
if err := hs.doFullHandshake(); err != nil {
return err
}
if err := hs.establishKeys(); err != nil {
return err
}
if err := hs.readFinished(); err != nil {
return err
}
if err := hs.sendSessionTicket(); err != nil {
return err
}
if err := hs.sendFinished(); err != nil {
return err
}
}
c.handshakeComplete = true
return nil
}
// readClientHello reads a ClientHello message from the client and decides
// whether we will perform session resumption.
func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
config := hs.c.config
c := hs.c
msg, err := c.readHandshake()
if err != nil {
return false, err
}
var ok bool
hs.clientHello, ok = msg.(*clientHelloMsg)
if !ok {
return false, c.sendAlert(alertUnexpectedMessage)
}
c.vers, ok = mutualVersion(hs.clientHello.vers)
if !ok {
return false, c.sendAlert(alertProtocolVersion)
} }
c.vers = vers
c.haveVers = true c.haveVers = true
finishedHash := newFinishedHash(vers) hs.finishedHash = newFinishedHash(c.vers)
finishedHash.Write(clientHello.marshal()) hs.finishedHash.Write(hs.clientHello.marshal())
hello := new(serverHelloMsg) hs.hello = new(serverHelloMsg)
supportedCurve := false supportedCurve := false
Curves: Curves:
for _, curve := range clientHello.supportedCurves { for _, curve := range hs.clientHello.supportedCurves {
switch curve { switch curve {
case curveP256, curveP384, curveP521: case curveP256, curveP384, curveP521:
supportedCurve = true supportedCurve = true
@ -46,110 +135,173 @@ Curves:
} }
supportedPointFormat := false supportedPointFormat := false
for _, pointFormat := range clientHello.supportedPoints { for _, pointFormat := range hs.clientHello.supportedPoints {
if pointFormat == pointFormatUncompressed { if pointFormat == pointFormatUncompressed {
supportedPointFormat = true supportedPointFormat = true
break break
} }
} }
hs.ellipticOk = supportedCurve && supportedPointFormat
ellipticOk := supportedCurve && supportedPointFormat
var suite *cipherSuite
FindCipherSuite:
for _, id := range clientHello.cipherSuites {
for _, supported := range config.cipherSuites() {
if id == supported {
var candidate *cipherSuite
for _, s := range cipherSuites {
if s.id == id {
candidate = s
break
}
}
if candidate == nil {
continue
}
// Don't select a ciphersuite which we can't
// support for this client.
if candidate.elliptic && !ellipticOk {
continue
}
suite = candidate
break FindCipherSuite
}
}
}
foundCompression := false foundCompression := false
// We only support null compression, so check that the client offered it. // We only support null compression, so check that the client offered it.
for _, compression := range clientHello.compressionMethods { for _, compression := range hs.clientHello.compressionMethods {
if compression == compressionNone { if compression == compressionNone {
foundCompression = true foundCompression = true
break break
} }
} }
if suite == nil || !foundCompression { if !foundCompression {
return c.sendAlert(alertHandshakeFailure) return false, c.sendAlert(alertHandshakeFailure)
} }
hello.vers = vers hs.hello.vers = c.vers
hello.cipherSuite = suite.id
t := uint32(config.time().Unix()) t := uint32(config.time().Unix())
hello.random = make([]byte, 32) hs.hello.random = make([]byte, 32)
hello.random[0] = byte(t >> 24) hs.hello.random[0] = byte(t >> 24)
hello.random[1] = byte(t >> 16) hs.hello.random[1] = byte(t >> 16)
hello.random[2] = byte(t >> 8) hs.hello.random[2] = byte(t >> 8)
hello.random[3] = byte(t) hs.hello.random[3] = byte(t)
_, err = io.ReadFull(config.rand(), hello.random[4:]) _, err = io.ReadFull(config.rand(), hs.hello.random[4:])
if err != nil { if err != nil {
return c.sendAlert(alertInternalError) return false, c.sendAlert(alertInternalError)
} }
hello.compressionMethod = compressionNone hs.hello.compressionMethod = compressionNone
if clientHello.nextProtoNeg { if len(hs.clientHello.serverName) > 0 {
hello.nextProtoNeg = true c.serverName = hs.clientHello.serverName
hello.nextProtos = config.NextProtos
} }
if hs.clientHello.nextProtoNeg {
hs.hello.nextProtoNeg = true
hs.hello.nextProtos = config.NextProtos
}
if hs.checkForResumption() {
return true, nil
}
for _, id := range hs.clientHello.cipherSuites {
if hs.suite = c.tryCipherSuite(id, hs.ellipticOk); hs.suite != nil {
break
}
}
if hs.suite == nil {
return false, c.sendAlert(alertHandshakeFailure)
}
return false, nil
}
// checkForResumption returns true if we should perform resumption on this connection.
func (hs *serverHandshakeState) checkForResumption() bool {
c := hs.c
var ok bool
if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !ok {
return false
}
if hs.sessionState.vers > hs.clientHello.vers {
return false
}
if vers, ok := mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers {
return false
}
cipherSuiteOk := false
// Check that the client is still offering the ciphersuite in the session.
for _, id := range hs.clientHello.cipherSuites {
if id == hs.sessionState.cipherSuite {
cipherSuiteOk = true
break
}
}
if !cipherSuiteOk {
return false
}
// Check that we also support the ciphersuite from the session.
hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, hs.ellipticOk)
if hs.suite == nil {
return false
}
sessionHasClientCerts := len(hs.sessionState.certificates) != 0
needClientCerts := c.config.ClientAuth == RequireAnyClientCert || c.config.ClientAuth == RequireAndVerifyClientCert
if needClientCerts && !sessionHasClientCerts {
return false
}
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
return false
}
return true
}
func (hs *serverHandshakeState) doResumeHandshake() error {
c := hs.c
hs.hello.cipherSuite = hs.suite.id
// We echo the client's session ID in the ServerHello to let it know
// that we're doing a resumption.
hs.hello.sessionId = hs.clientHello.sessionId
hs.finishedHash.Write(hs.hello.marshal())
c.writeRecord(recordTypeHandshake, hs.hello.marshal())
if len(hs.sessionState.certificates) > 0 {
if _, err := hs.processCertsFromClient(hs.sessionState.certificates); err != nil {
return err
}
}
hs.masterSecret = hs.sessionState.masterSecret
return nil
}
func (hs *serverHandshakeState) doFullHandshake() error {
config := hs.c.config
c := hs.c
if len(config.Certificates) == 0 { if len(config.Certificates) == 0 {
return c.sendAlert(alertInternalError) return c.sendAlert(alertInternalError)
} }
cert := &config.Certificates[0] cert := &config.Certificates[0]
if len(clientHello.serverName) > 0 { if len(hs.clientHello.serverName) > 0 {
c.serverName = clientHello.serverName cert = config.getCertificateForName(hs.clientHello.serverName)
cert = config.getCertificateForName(clientHello.serverName)
} }
if clientHello.ocspStapling && len(cert.OCSPStaple) > 0 { if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
hello.ocspStapling = true hs.hello.ocspStapling = true
} }
finishedHash.Write(hello.marshal()) hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled
c.writeRecord(recordTypeHandshake, hello.marshal()) hs.hello.cipherSuite = hs.suite.id
hs.finishedHash.Write(hs.hello.marshal())
c.writeRecord(recordTypeHandshake, hs.hello.marshal())
certMsg := new(certificateMsg) certMsg := new(certificateMsg)
certMsg.certificates = cert.Certificate certMsg.certificates = cert.Certificate
finishedHash.Write(certMsg.marshal()) hs.finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal())
if hello.ocspStapling { if hs.hello.ocspStapling {
certStatus := new(certificateStatusMsg) certStatus := new(certificateStatusMsg)
certStatus.statusType = statusTypeOCSP certStatus.statusType = statusTypeOCSP
certStatus.response = cert.OCSPStaple certStatus.response = cert.OCSPStaple
finishedHash.Write(certStatus.marshal()) hs.finishedHash.Write(certStatus.marshal())
c.writeRecord(recordTypeHandshake, certStatus.marshal()) c.writeRecord(recordTypeHandshake, certStatus.marshal())
} }
keyAgreement := suite.ka() keyAgreement := hs.suite.ka()
skx, err := keyAgreement.generateServerKeyExchange(config, cert, clientHello, hello) skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello)
if err != nil { if err != nil {
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
} }
if skx != nil { if skx != nil {
finishedHash.Write(skx.marshal()) hs.finishedHash.Write(skx.marshal())
c.writeRecord(recordTypeHandshake, skx.marshal()) c.writeRecord(recordTypeHandshake, skx.marshal())
} }
@ -166,28 +318,29 @@ FindCipherSuite:
if config.ClientCAs != nil { if config.ClientCAs != nil {
certReq.certificateAuthorities = config.ClientCAs.Subjects() certReq.certificateAuthorities = config.ClientCAs.Subjects()
} }
finishedHash.Write(certReq.marshal()) hs.finishedHash.Write(certReq.marshal())
c.writeRecord(recordTypeHandshake, certReq.marshal()) c.writeRecord(recordTypeHandshake, certReq.marshal())
} }
helloDone := new(serverHelloDoneMsg) helloDone := new(serverHelloDoneMsg)
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 *rsa.PublicKey // public key for client auth, if any
msg, err = c.readHandshake() msg, err := c.readHandshake()
if err != nil { if err != nil {
return err return err
} }
var ok bool
// If we requested a client certificate, then the client must send a // If we requested a client certificate, then the client must send a
// certificate message, even if it's empty. // certificate message, even if it's empty.
if config.ClientAuth >= RequestClientCert { if config.ClientAuth >= RequestClientCert {
if certMsg, ok = msg.(*certificateMsg); !ok { if certMsg, ok = msg.(*certificateMsg); !ok {
return c.sendAlert(alertHandshakeFailure) return c.sendAlert(alertHandshakeFailure)
} }
finishedHash.Write(certMsg.marshal()) hs.finishedHash.Write(certMsg.marshal())
if len(certMsg.certificates) == 0 { if len(certMsg.certificates) == 0 {
// The client didn't actually send a certificate // The client didn't actually send a certificate
@ -198,54 +351,9 @@ FindCipherSuite:
} }
} }
certs := make([]*x509.Certificate, len(certMsg.certificates)) pub, err = hs.processCertsFromClient(certMsg.certificates)
for i, asn1Data := range certMsg.certificates {
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
c.sendAlert(alertBadCertificate)
return errors.New("tls: failed to parse client certificate: " + err.Error())
}
}
if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
opts := x509.VerifyOptions{
Roots: c.config.ClientCAs,
CurrentTime: c.config.time(),
Intermediates: x509.NewCertPool(),
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
chains, err := certs[0].Verify(opts)
if err != nil { if err != nil {
c.sendAlert(alertBadCertificate) return err
return errors.New("tls: failed to verify client's certificate: " + err.Error())
}
ok := false
for _, ku := range certs[0].ExtKeyUsage {
if ku == x509.ExtKeyUsageClientAuth {
ok = true
break
}
}
if !ok {
c.sendAlert(alertHandshakeFailure)
return errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication")
}
c.verifiedChains = chains
}
if len(certs) > 0 {
if pub, ok = certs[0].PublicKey.(*rsa.PublicKey); !ok {
return c.sendAlert(alertUnsupportedCertificate)
}
c.peerCertificates = certs
} }
msg, err = c.readHandshake() msg, err = c.readHandshake()
@ -259,7 +367,7 @@ FindCipherSuite:
if !ok { if !ok {
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
finishedHash.Write(ckx.marshal()) hs.finishedHash.Write(ckx.marshal())
// 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
@ -278,15 +386,15 @@ FindCipherSuite:
} }
digest := make([]byte, 0, 36) digest := make([]byte, 0, 36)
digest = finishedHash.serverMD5.Sum(digest) digest = hs.finishedHash.serverMD5.Sum(digest)
digest = finishedHash.serverSHA1.Sum(digest) digest = hs.finishedHash.serverSHA1.Sum(digest)
err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, 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())
} }
finishedHash.Write(certVerify.marshal()) hs.finishedHash.Write(certVerify.marshal())
} }
preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers) preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers)
@ -294,20 +402,38 @@ FindCipherSuite:
c.sendAlert(alertHandshakeFailure) c.sendAlert(alertHandshakeFailure)
return err return err
} }
hs.masterSecret = masterFromPreMasterSecret(c.vers, preMasterSecret, hs.clientHello.random, hs.hello.random)
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := return nil
keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen) }
clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */) func (hs *serverHandshakeState) establishKeys() error {
clientHash := suite.mac(c.vers, clientMAC) c := hs.c
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)
clientCipher := hs.suite.cipher(clientKey, clientIV, true /* for reading */)
clientHash := hs.suite.mac(c.vers, clientMAC)
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
serverCipher := hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
serverHash := hs.suite.mac(c.vers, serverMAC)
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
return nil
}
func (hs *serverHandshakeState) readFinished() error {
c := hs.c
c.readRecord(recordTypeChangeCipherSpec) c.readRecord(recordTypeChangeCipherSpec)
if err := c.error(); err != nil { if err := c.error(); err != nil {
return err return err
} }
if hello.nextProtoNeg { if hs.hello.nextProtoNeg {
msg, err = c.readHandshake() msg, err := c.readHandshake()
if err != nil { if err != nil {
return err return err
} }
@ -315,11 +441,11 @@ FindCipherSuite:
if !ok { if !ok {
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
finishedHash.Write(nextProto.marshal()) hs.finishedHash.Write(nextProto.marshal())
c.clientProtocol = nextProto.proto c.clientProtocol = nextProto.proto
} }
msg, err = c.readHandshake() msg, err := c.readHandshake()
if err != nil { if err != nil {
return err return err
} }
@ -328,25 +454,142 @@ FindCipherSuite:
return c.sendAlert(alertUnexpectedMessage) return c.sendAlert(alertUnexpectedMessage)
} }
verify := finishedHash.clientSum(masterSecret) verify := hs.finishedHash.clientSum(hs.masterSecret)
if len(verify) != len(clientFinished.verifyData) || if len(verify) != len(clientFinished.verifyData) ||
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 { subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
return c.sendAlert(alertHandshakeFailure) return c.sendAlert(alertHandshakeFailure)
} }
finishedHash.Write(clientFinished.marshal()) hs.finishedHash.Write(clientFinished.marshal())
return nil
}
serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */) func (hs *serverHandshakeState) sendSessionTicket() error {
serverHash := suite.mac(c.vers, serverMAC) if !hs.hello.ticketSupported {
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) return nil
c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) }
finished := new(finishedMsg) c := hs.c
finished.verifyData = finishedHash.serverSum(masterSecret) m := new(newSessionTicketMsg)
c.writeRecord(recordTypeHandshake, finished.marshal())
c.handshakeComplete = true var err error
c.cipherSuite = suite.id state := sessionState{
vers: c.vers,
cipherSuite: hs.suite.id,
masterSecret: hs.masterSecret,
certificates: hs.certsFromClient,
}
m.ticket, err = c.encryptTicket(&state)
if err != nil {
return err
}
hs.finishedHash.Write(m.marshal())
c.writeRecord(recordTypeHandshake, m.marshal())
return nil
}
func (hs *serverHandshakeState) sendFinished() error {
c := hs.c
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
finished := new(finishedMsg)
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
hs.finishedHash.Write(finished.marshal())
c.writeRecord(recordTypeHandshake, finished.marshal())
c.cipherSuite = hs.suite.id
return nil
}
// processCertsFromClient takes a chain of client certificates either from a
// Certificates message or from a sessionState and verifies them. It returns
// the public key of the leaf certificate.
func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*rsa.PublicKey, error) {
c := hs.c
hs.certsFromClient = certificates
certs := make([]*x509.Certificate, len(certificates))
var err error
for i, asn1Data := range certificates {
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
c.sendAlert(alertBadCertificate)
return nil, errors.New("tls: failed to parse client certificate: " + err.Error())
}
}
if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
opts := x509.VerifyOptions{
Roots: c.config.ClientCAs,
CurrentTime: c.config.time(),
Intermediates: x509.NewCertPool(),
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
for _, cert := range certs[1:] {
opts.Intermediates.AddCert(cert)
}
chains, err := certs[0].Verify(opts)
if err != nil {
c.sendAlert(alertBadCertificate)
return nil, errors.New("tls: failed to verify client's certificate: " + err.Error())
}
ok := false
for _, ku := range certs[0].ExtKeyUsage {
if ku == x509.ExtKeyUsageClientAuth {
ok = true
break
}
}
if !ok {
c.sendAlert(alertHandshakeFailure)
return nil, errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication")
}
c.verifiedChains = chains
}
if len(certs) > 0 {
pub, ok := certs[0].PublicKey.(*rsa.PublicKey)
if !ok {
return nil, c.sendAlert(alertUnsupportedCertificate)
}
c.peerCertificates = certs
return pub, nil
}
return nil, nil
}
// tryCipherSuite returns a cipherSuite with the given id if that cipher suite
// is acceptable to use.
func (c *Conn) tryCipherSuite(id uint16, ellipticOk bool) *cipherSuite {
for _, supported := range c.config.cipherSuites() {
if id == supported {
var candidate *cipherSuite
for _, s := range cipherSuites {
if s.id == id {
candidate = s
break
}
}
if candidate == nil {
continue
}
// Don't select a ciphersuite which we can't
// support for this client.
if candidate.elliptic && !ellipticOk {
continue
}
return candidate
}
}
return nil return nil
} }

View File

@ -11,12 +11,15 @@ import (
"encoding/hex" "encoding/hex"
"encoding/pem" "encoding/pem"
"flag" "flag"
"fmt"
"io" "io"
"log" "log"
"math/big" "math/big"
"net" "net"
"os"
"strconv" "strconv"
"strings" "strings"
"sync"
"testing" "testing"
"time" "time"
) )
@ -80,13 +83,20 @@ func TestRejectBadProtocolVersion(t *testing.T) {
} }
func TestNoSuiteOverlap(t *testing.T) { func TestNoSuiteOverlap(t *testing.T) {
clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{0xff00}, []uint8{0}, false, "", false, nil, nil} clientHello := &clientHelloMsg{
vers: 0x0301,
cipherSuites: []uint16{0xff00},
compressionMethods: []uint8{0},
}
testClientHelloFailure(t, clientHello, alertHandshakeFailure) testClientHelloFailure(t, clientHello, alertHandshakeFailure)
} }
func TestNoCompressionOverlap(t *testing.T) { func TestNoCompressionOverlap(t *testing.T) {
clientHello := &clientHelloMsg{nil, 0x0301, nil, nil, []uint16{TLS_RSA_WITH_RC4_128_SHA}, []uint8{0xff}, false, "", false, nil, nil} clientHello := &clientHelloMsg{
vers: 0x0301,
cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA},
compressionMethods: []uint8{0xff},
}
testClientHelloFailure(t, clientHello, alertHandshakeFailure) testClientHelloFailure(t, clientHello, alertHandshakeFailure)
} }
@ -186,6 +196,11 @@ func TestHandshakeServerSNI(t *testing.T) {
testServerScript(t, "SNI", selectCertificateBySNIScript, testConfig, nil) testServerScript(t, "SNI", selectCertificateBySNIScript, testConfig, nil)
} }
func TestResumption(t *testing.T) {
testServerScript(t, "IssueTicket", issueSessionTicketTest, testConfig, nil)
testServerScript(t, "Resume", serverResumeTest, testConfig, nil)
}
type clientauthTest struct { type clientauthTest struct {
name string name string
clientauth ClientAuthType clientauth ClientAuthType
@ -203,55 +218,133 @@ func TestClientAuth(t *testing.T) {
} }
} }
// recordingConn is a net.Conn that records the traffic that passes through it.
// WriteTo can be used to produce Go code that contains the recorded traffic.
type recordingConn struct {
net.Conn
lock sync.Mutex
flows [][]byte
currentlyReading bool
}
func (r *recordingConn) Read(b []byte) (n int, err error) {
if n, err = r.Conn.Read(b); n == 0 {
return
}
b = b[:n]
r.lock.Lock()
defer r.lock.Unlock()
if l := len(r.flows); l == 0 || !r.currentlyReading {
buf := make([]byte, len(b))
copy(buf, b)
r.flows = append(r.flows, buf)
} else {
r.flows[l-1] = append(r.flows[l-1], b[:n]...)
}
r.currentlyReading = true
return
}
func (r *recordingConn) Write(b []byte) (n int, err error) {
if n, err = r.Conn.Write(b); n == 0 {
return
}
b = b[:n]
r.lock.Lock()
defer r.lock.Unlock()
if l := len(r.flows); l == 0 || r.currentlyReading {
buf := make([]byte, len(b))
copy(buf, b)
r.flows = append(r.flows, buf)
} else {
r.flows[l-1] = append(r.flows[l-1], b[:n]...)
}
r.currentlyReading = false
return
}
// WriteTo writes Go source code to w that contains the recorded traffic.
func (r *recordingConn) WriteTo(w io.Writer) {
fmt.Fprintf(w, "var changeMe = [][]byte {\n")
for _, buf := range r.flows {
fmt.Fprintf(w, "\t{")
for i, b := range buf {
if i%8 == 0 {
fmt.Fprintf(w, "\n\t\t")
}
fmt.Fprintf(w, "0x%02x, ", b)
}
fmt.Fprintf(w, "\n\t},\n")
}
fmt.Fprintf(w, "}\n")
}
var serve = flag.Bool("serve", false, "run a TLS server on :10443") var serve = flag.Bool("serve", false, "run a TLS server on :10443")
var testCipherSuites = flag.String("ciphersuites", var testCipherSuites = flag.String("ciphersuites",
"0x"+strconv.FormatInt(int64(TLS_RSA_WITH_RC4_128_SHA), 16), "0x"+strconv.FormatInt(int64(TLS_RSA_WITH_RC4_128_SHA), 16),
"cipher suites to accept in serving mode") "cipher suites to accept in serving mode")
var testClientAuth = flag.Int("clientauth", 0, "value for tls.Config.ClientAuth") var testClientAuth = flag.Int("clientauth", 0, "value for tls.Config.ClientAuth")
func TestRunServer(t *testing.T) { func GetTestConfig() *Config {
if !*serve { var config = *testConfig
return
}
suites := strings.Split(*testCipherSuites, ",") suites := strings.Split(*testCipherSuites, ",")
testConfig.CipherSuites = make([]uint16, len(suites)) config.CipherSuites = make([]uint16, len(suites))
for i := range suites { for i := range suites {
suite, err := strconv.ParseUint(suites[i], 0, 64) suite, err := strconv.ParseUint(suites[i], 0, 64)
if err != nil { if err != nil {
panic(err) panic(err)
} }
testConfig.CipherSuites[i] = uint16(suite) config.CipherSuites[i] = uint16(suite)
} }
testConfig.ClientAuth = ClientAuthType(*testClientAuth) config.ClientAuth = ClientAuthType(*testClientAuth)
return &config
}
l, err := Listen("tcp", ":10443", testConfig) func TestRunServer(t *testing.T) {
if !*serve {
return
}
config := GetTestConfig()
const addr = ":10443"
l, err := net.Listen("tcp", addr)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
log.Printf("Now listening for connections on %s", addr)
for { for {
c, err := l.Accept() tcpConn, err := l.Accept()
if err != nil { if err != nil {
log.Printf("error accepting connection: %s", err)
break
}
record := &recordingConn{
Conn: tcpConn,
}
conn := Server(record, config)
if err := conn.Handshake(); err != nil {
log.Printf("error from TLS handshake: %s", err) log.Printf("error from TLS handshake: %s", err)
break break
} }
_, err = c.Write([]byte("hello, world\n")) _, err = conn.Write([]byte("hello, world\n"))
if err != nil { if err != nil {
log.Printf("error from TLS: %s", err) log.Printf("error from Write: %s", err)
continue continue
} }
st := c.(*Conn).ConnectionState() conn.Close()
if len(st.PeerCertificates) > 0 {
log.Print("Handling request from client ", st.PeerCertificates[0].Subject.CommonName)
} else {
log.Print("Handling request from anon client")
}
c.Close() record.WriteTo(os.Stdout)
} }
} }
@ -296,10 +389,8 @@ func loadPEMCert(in string) *x509.Certificate {
// Script of interaction with gnutls implementation. // Script of interaction with gnutls implementation.
// The values for this test are obtained by building and running in server mode: // The values for this test are obtained by building and running in server mode:
// % go test -run "TestRunServer" -serve // % go test -test.run "TestRunServer" -serve
// and then: // The recorded bytes are written to stdout.
// % gnutls-cli --insecure --debug 100 -p 10443 localhost > /tmp/log 2>&1
// % python parse-gnutls-cli-debug-log.py < /tmp/log
var rc4ServerScript = [][]byte{ var rc4ServerScript = [][]byte{
{ {
0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00, 0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00,
@ -961,21 +1052,37 @@ var sslv3ServerScript = [][]byte{
var selectCertificateBySNIScript = [][]byte{ var selectCertificateBySNIScript = [][]byte{
{ {
0x16, 0x03, 0x01, 0x00, 0x6e, 0x01, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, 0xed, 0x01, 0x00, 0x00,
0x6a, 0x03, 0x01, 0x4f, 0x85, 0xc4, 0xc2, 0xb9, 0xe9, 0x03, 0x02, 0x50, 0x5a, 0x1c, 0x90, 0x2b,
0x39, 0x80, 0x91, 0x66, 0x65, 0x56, 0x8e, 0xdd, 0xc8, 0xf1, 0xd9, 0x4b, 0xd0, 0x18, 0x69, 0xed,
0x48, 0xe9, 0xca, 0x34, 0x02, 0x3c, 0xaf, 0x0d, 0x5a, 0xbd, 0x68, 0xf6, 0xf7, 0xe3, 0xf0, 0x6e,
0x73, 0xb5, 0x2a, 0x05, 0x6e, 0xbd, 0x5e, 0x8f, 0xd1, 0xcc, 0xf1, 0x2d, 0x94, 0xa4, 0x01, 0x63,
0x38, 0xf9, 0xe5, 0x00, 0x00, 0x28, 0x00, 0x39, 0x91, 0xbe, 0xd0, 0x00, 0x00, 0x66, 0xc0, 0x14,
0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, 0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21, 0x00, 0x39,
0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x88, 0x00, 0x87, 0xc0, 0x0f,
0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0xc0, 0x05, 0x00, 0x35, 0x00, 0x84, 0xc0, 0x12,
0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0xc0, 0x08, 0xc0, 0x1c, 0xc0, 0x1b, 0x00, 0x16,
0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, 0x00, 0x13, 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a,
0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, 0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x1e,
0x0e, 0x00, 0x00, 0x0b, 0x73, 0x6e, 0x69, 0x74, 0x00, 0x33, 0x00, 0x32, 0x00, 0x9a, 0x00, 0x99,
0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x45, 0x00, 0x44, 0xc0, 0x0e, 0xc0, 0x04,
0x23, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x96, 0x00, 0x41, 0xc0, 0x11,
0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02, 0x00, 0x05,
0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09,
0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x06,
0x00, 0x03, 0x00, 0xff, 0x02, 0x01, 0x00, 0x00,
0x59, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00,
0x00, 0x0b, 0x73, 0x6e, 0x69, 0x74, 0x65, 0x73,
0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x0b, 0x00,
0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00,
0x34, 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, 0x00,
0x19, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, 0x00,
0x09, 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, 0x00,
0x08, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00,
0x15, 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, 0x00,
0x13, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00,
0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x0f, 0x00,
0x01, 0x01,
}, },
{ {
0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
@ -1053,45 +1160,323 @@ var selectCertificateBySNIScript = [][]byte{
}, },
{ {
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
0x82, 0x00, 0x80, 0x70, 0x1d, 0x34, 0x75, 0xa2, 0x82, 0x00, 0x80, 0x45, 0x6d, 0x68, 0x61, 0xb9,
0xe7, 0xe3, 0x2f, 0x3d, 0xc1, 0x1d, 0xca, 0x0b, 0x1a, 0xe5, 0xeb, 0x67, 0x22, 0x3b, 0x87, 0x19,
0xe3, 0x64, 0xb9, 0x1a, 0x00, 0x69, 0xc4, 0x14, 0x52, 0x86, 0x31, 0x91, 0xee, 0xcd, 0x17, 0x75,
0x05, 0x07, 0x7e, 0xc3, 0x51, 0x43, 0x52, 0x66, 0xc6, 0x44, 0xaf, 0x23, 0xef, 0xd9, 0xfa, 0xd2,
0xe3, 0xbd, 0xff, 0x1b, 0x1a, 0x6a, 0x84, 0xf2, 0x0b, 0xa2, 0xbb, 0xbf, 0x8b, 0x4b, 0x34, 0x50,
0x07, 0x24, 0xd7, 0x12, 0xa8, 0x58, 0xcf, 0x8a, 0xf6, 0x2e, 0x05, 0x09, 0x7e, 0xbf, 0xb3, 0xa6,
0x50, 0x30, 0xe8, 0xc8, 0xb2, 0xf9, 0x58, 0x1c, 0x10, 0xe3, 0xc3, 0x49, 0x55, 0xa8, 0xdf, 0x6c,
0x56, 0x53, 0x76, 0x21, 0xe0, 0x03, 0x7f, 0x77, 0xaa, 0xab, 0x11, 0x4c, 0x80, 0x0a, 0x45, 0xf8,
0xa7, 0xf1, 0xad, 0x67, 0xd4, 0xe2, 0x8f, 0xa0, 0x37, 0xbb, 0xd3, 0x18, 0x4e, 0xec, 0x51, 0xbf,
0x58, 0x6c, 0xe0, 0x28, 0x59, 0xf3, 0xd1, 0x53, 0x1a, 0xf6, 0x11, 0x1b, 0xcf, 0x2c, 0xaf, 0x5f,
0x2b, 0x21, 0xbd, 0xa3, 0x84, 0x31, 0x73, 0xbf, 0x0b, 0x52, 0x4e, 0x92, 0x0c, 0x7a, 0xb2, 0x5d,
0x84, 0x0f, 0x83, 0xf4, 0xc4, 0xd0, 0xe5, 0x3c, 0xe2, 0x1f, 0x83, 0xbe, 0xf5, 0xbf, 0x05, 0xbf,
0x2d, 0x3e, 0xf2, 0x8a, 0x1e, 0xe7, 0xe9, 0x1f, 0x99, 0xd6, 0x9c, 0x86, 0x47, 0x5e, 0xb4, 0xff,
0x12, 0x13, 0xad, 0x29, 0xd6, 0x0c, 0xc7, 0xc6, 0xe7, 0xac, 0xad, 0x1e, 0x3c, 0xaa, 0x91, 0x39,
0x05, 0x53, 0x7d, 0x5e, 0xc6, 0x92, 0x72, 0xba, 0xca, 0xad, 0xc5, 0x54, 0x64, 0x7e, 0xc2, 0x8a,
0xd2, 0x93, 0x8f, 0x53, 0x84, 0x87, 0x44, 0x05, 0x48, 0xee, 0xb6, 0x4e, 0xf9, 0x33, 0x82, 0x52,
0x9f, 0x5d, 0x66, 0x14, 0x03, 0x01, 0x00, 0x01, 0xe8, 0xed, 0x48, 0x14, 0x03, 0x01, 0x00, 0x01,
0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xfc, 0x71, 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xc1, 0x2f,
0xaa, 0xa8, 0x37, 0xa8, 0xbd, 0x63, 0xb7, 0xbc, 0x34, 0x03, 0x2a, 0xf2, 0xfd, 0x83, 0x69, 0x23,
0x95, 0xef, 0x0c, 0xcf, 0x39, 0x31, 0x93, 0xe6, 0x8c, 0x9e, 0x66, 0x3b, 0xbb, 0xd1, 0xab, 0xbb,
0x86, 0xbd, 0x3f, 0x56, 0x9d, 0xf0, 0xb2, 0xb5, 0x51, 0x89, 0x27, 0x88, 0x0f, 0x08, 0x3e, 0x00,
0xd1, 0xa7, 0xc6, 0x45, 0x89, 0x18, 0xfb, 0xa0, 0xdc, 0xc7, 0x47, 0x82, 0x13, 0x34, 0xec, 0xca,
0x7f, 0xc1, 0x68, 0x6a,
}, },
{ {
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
0x01, 0x00, 0x24, 0xb8, 0x6d, 0x9a, 0x90, 0x3c, 0x01, 0x00, 0x24, 0xda, 0x61, 0x76, 0x9f, 0x7a,
0x45, 0xe0, 0xff, 0x63, 0xba, 0xab, 0x3d, 0x7a, 0x8a, 0xd0, 0x5f, 0x9b, 0x3d, 0xa7, 0xd5, 0xdd,
0xa6, 0x49, 0x5a, 0x13, 0xdc, 0x0e, 0xa3, 0xba, 0x95, 0x4b, 0xd4, 0x64, 0x2d, 0x2d, 0x6a, 0x98,
0x7f, 0x04, 0x19, 0x45, 0xfd, 0xfb, 0xbd, 0x00, 0x9e, 0xfe, 0x77, 0x76, 0xe3, 0x02, 0x05, 0x0c,
0xa3, 0xa7, 0x78, 0x81, 0x38, 0x9f, 0x10, 0x17, 0xb2, 0xa6, 0x15, 0x82, 0x28, 0x25, 0xc5, 0x17,
0x03, 0x01, 0x00, 0x21, 0x43, 0xc3, 0x91, 0xb7, 0x03, 0x01, 0x00, 0x21, 0x4e, 0x66, 0x2d, 0x50,
0xbf, 0x50, 0x0b, 0x04, 0xb4, 0x5d, 0xc6, 0x20, 0x00, 0xa2, 0x44, 0x4d, 0xee, 0x5f, 0x81, 0x67,
0x64, 0xb8, 0x01, 0x09, 0x25, 0x2c, 0x03, 0x30, 0x21, 0x5d, 0x94, 0xc0, 0xfb, 0xdc, 0xbd, 0xf6,
0xc0, 0x77, 0xc9, 0x5e, 0xe6, 0xe0, 0x99, 0xdc, 0xa8, 0x32, 0x8e, 0x2c, 0x22, 0x58, 0x37, 0xb6,
0xcd, 0x75, 0x9d, 0x51, 0x82, 0x15, 0x03, 0x01, 0xa3, 0x1e, 0xf8, 0xdd, 0x83, 0x15, 0x03, 0x01,
0x00, 0x16, 0x2d, 0x7a, 0x89, 0x7b, 0x36, 0x85, 0x00, 0x16, 0x68, 0x3b, 0x3a, 0xd0, 0x1e, 0xc4,
0x2a, 0x93, 0xcb, 0x83, 0xa7, 0x2f, 0x9e, 0x91, 0x5e, 0x97, 0x6a, 0x47, 0x38, 0xfe, 0x17, 0x8e,
0xfc, 0xad, 0x57, 0xca, 0xf5, 0xbc, 0x13, 0x2f, 0xc0, 0xb6, 0x4a, 0x94, 0x00, 0xb5, 0x91, 0xbf,
},
}
var issueSessionTicketTest = [][]byte{
{
0x16, 0x03, 0x01, 0x00, 0xdd, 0x01, 0x00, 0x00,
0xd9, 0x03, 0x02, 0x50, 0x5a, 0x32, 0xb6, 0x36,
0x0e, 0x94, 0x63, 0x57, 0x93, 0xd7, 0x1e, 0xb2,
0xa7, 0xd3, 0x20, 0x24, 0x30, 0x3f, 0x46, 0xf9,
0xfe, 0x22, 0x02, 0xa1, 0xff, 0x57, 0xf8, 0x8f,
0x95, 0x4c, 0xdd, 0x00, 0x00, 0x66, 0xc0, 0x14,
0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21, 0x00, 0x39,
0x00, 0x38, 0x00, 0x88, 0x00, 0x87, 0xc0, 0x0f,
0xc0, 0x05, 0x00, 0x35, 0x00, 0x84, 0xc0, 0x12,
0xc0, 0x08, 0xc0, 0x1c, 0xc0, 0x1b, 0x00, 0x16,
0x00, 0x13, 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a,
0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x1e,
0x00, 0x33, 0x00, 0x32, 0x00, 0x9a, 0x00, 0x99,
0x00, 0x45, 0x00, 0x44, 0xc0, 0x0e, 0xc0, 0x04,
0x00, 0x2f, 0x00, 0x96, 0x00, 0x41, 0xc0, 0x11,
0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02, 0x00, 0x05,
0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09,
0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x06,
0x00, 0x03, 0x00, 0xff, 0x02, 0x01, 0x00, 0x00,
0x49, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
0x02, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32, 0x00,
0x0e, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b, 0x00,
0x0c, 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a, 0x00,
0x16, 0x00, 0x17, 0x00, 0x08, 0x00, 0x06, 0x00,
0x07, 0x00, 0x14, 0x00, 0x15, 0x00, 0x04, 0x00,
0x05, 0x00, 0x12, 0x00, 0x13, 0x00, 0x01, 0x00,
0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10, 0x00,
0x11, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, 0x00,
0x01, 0x01,
},
{
0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00,
0x00,
},
{
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
0x82, 0x00, 0x80, 0x92, 0x3f, 0xcc, 0x4d, 0x2f,
0xb2, 0x12, 0xc4, 0xf5, 0x72, 0xf3, 0x5a, 0x3c,
0x5a, 0xbb, 0x99, 0x89, 0xe6, 0x21, 0x0f, 0xdf,
0xf3, 0xa3, 0xd0, 0xce, 0x76, 0x55, 0xfd, 0xec,
0x38, 0x80, 0xf0, 0x46, 0x0b, 0xfa, 0x61, 0x7c,
0xc2, 0xb5, 0xe2, 0x89, 0x7b, 0xeb, 0xcf, 0x3e,
0x97, 0xab, 0x72, 0xf6, 0xfd, 0xcf, 0x10, 0x82,
0x3a, 0x05, 0x55, 0x7c, 0x2d, 0x7f, 0x44, 0x38,
0x9d, 0xeb, 0xa4, 0x7e, 0x53, 0x35, 0xda, 0xe0,
0x7c, 0x24, 0x66, 0x42, 0x5d, 0x85, 0xcf, 0xa6,
0x98, 0x81, 0xec, 0x42, 0x94, 0x4e, 0x25, 0xb1,
0x64, 0xac, 0x89, 0x98, 0x74, 0xd2, 0xeb, 0x51,
0x5a, 0xb3, 0xbd, 0x14, 0xf6, 0xc6, 0xec, 0x0b,
0xdd, 0x8b, 0x89, 0xdc, 0xde, 0xf3, 0xd6, 0x62,
0xee, 0xe3, 0xcf, 0xf5, 0x39, 0x23, 0x46, 0x4f,
0xb8, 0xef, 0x14, 0x39, 0x06, 0x36, 0xad, 0x84,
0x42, 0xb9, 0xd7, 0x14, 0x03, 0x01, 0x00, 0x01,
0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xa1, 0xf0,
0x68, 0xf5, 0x29, 0x7e, 0x78, 0xaa, 0xbd, 0x59,
0xdc, 0x32, 0xab, 0x8e, 0x25, 0x54, 0x64, 0x9e,
0x2b, 0x08, 0xf9, 0xb8, 0xe3, 0x89, 0x09, 0xa4,
0xfd, 0x05, 0x78, 0x59, 0xcb, 0x33, 0xfc, 0x66,
0xb5, 0x73,
},
{
0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00,
0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
0xe8, 0x4b, 0xd1, 0xef, 0xba, 0xfc, 0x00, 0xd4,
0x2f, 0xf5, 0x6f, 0xba, 0xdc, 0xb7, 0xd7, 0x87,
0x59, 0x58, 0x05, 0x06, 0x36, 0x8f, 0x47, 0xc7,
0x9e, 0x4c, 0xf8, 0xb5, 0xd7, 0x55, 0x84, 0x64,
0x0b, 0x4c, 0x0b, 0xad, 0x8d, 0x9b, 0x79, 0x4d,
0xd7, 0x61, 0xf7, 0x2b, 0x89, 0x46, 0x2b, 0x52,
0x1a, 0x3f, 0x51, 0x58, 0xce, 0x59, 0x23, 0xef,
0x60, 0x55, 0x07, 0xc0, 0x46, 0x97, 0xad, 0x0a,
0xe3, 0x55, 0x10, 0x06, 0xff, 0x57, 0x0c, 0xb1,
0x49, 0xac, 0x80, 0xc6, 0xc3, 0x95, 0x5f, 0x12,
0xe2, 0xe5, 0xaa, 0x9f, 0x78, 0xc2, 0x20, 0x14,
0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
0x00, 0x24, 0x47, 0x51, 0xf1, 0x13, 0xc8, 0xa6,
0xd2, 0x2c, 0xad, 0x35, 0xff, 0x53, 0xe2, 0x72,
0x01, 0xcb, 0x33, 0xcd, 0xf4, 0xa0, 0x9c, 0x03,
0x47, 0xfe, 0xcd, 0xc1, 0x46, 0x8d, 0x41, 0x5e,
0x54, 0xf7, 0xc3, 0x85, 0x2b, 0x2f, 0x17, 0x03,
0x01, 0x00, 0x21, 0xf4, 0xbf, 0x94, 0x3e, 0x93,
0x0b, 0x1b, 0x75, 0x3a, 0xd9, 0xd0, 0x57, 0x75,
0xf3, 0xa7, 0x82, 0xc9, 0x6b, 0x9e, 0x43, 0x98,
0x44, 0x9e, 0x9f, 0xad, 0x03, 0xa8, 0xb9, 0xa3,
0x0a, 0xd1, 0xc4, 0xb4, 0x15, 0x03, 0x01, 0x00,
0x16, 0xee, 0x57, 0xbd, 0xd3, 0xb7, 0x20, 0x29,
0xd1, 0x24, 0xe2, 0xdc, 0x24, 0xc3, 0x73, 0x86,
0x81, 0x8e, 0x40, 0xc3, 0x6e, 0x99, 0x9e,
},
}
var serverResumeTest = [][]byte{
{
0x16, 0x03, 0x01, 0x01, 0x65, 0x01, 0x00, 0x01,
0x61, 0x03, 0x01, 0x50, 0x5a, 0x32, 0xe2, 0xde,
0x19, 0x5c, 0xb6, 0x51, 0x87, 0xa4, 0x30, 0x2e,
0x95, 0x26, 0xd6, 0xed, 0xbf, 0xbf, 0x24, 0xbb,
0xd1, 0x1a, 0x29, 0x9f, 0x37, 0xfd, 0xfb, 0xae,
0xc2, 0xba, 0x2b, 0x20, 0xb5, 0x7a, 0x00, 0x96,
0x92, 0x51, 0xfc, 0x41, 0x16, 0x29, 0xc0, 0x54,
0x5e, 0xa7, 0xa9, 0x1f, 0xf8, 0xbf, 0x79, 0xfa,
0x49, 0x5a, 0x15, 0x28, 0x72, 0x9a, 0x59, 0xf9,
0x9b, 0xc4, 0x3a, 0xa8, 0x00, 0x66, 0xc0, 0x14,
0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21, 0x00, 0x39,
0x00, 0x38, 0x00, 0x88, 0x00, 0x87, 0xc0, 0x0f,
0xc0, 0x05, 0x00, 0x35, 0x00, 0x84, 0xc0, 0x12,
0xc0, 0x08, 0xc0, 0x1c, 0xc0, 0x1b, 0x00, 0x16,
0x00, 0x13, 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a,
0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x1e,
0x00, 0x33, 0x00, 0x32, 0x00, 0x9a, 0x00, 0x99,
0x00, 0x45, 0x00, 0x44, 0xc0, 0x0e, 0xc0, 0x04,
0x00, 0x2f, 0x00, 0x96, 0x00, 0x41, 0xc0, 0x11,
0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02, 0x00, 0x05,
0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09,
0x00, 0x14, 0x00, 0x11, 0x00, 0x08, 0x00, 0x06,
0x00, 0x03, 0x00, 0xff, 0x02, 0x01, 0x00, 0x00,
0xb1, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
0x02, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32, 0x00,
0x0e, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b, 0x00,
0x0c, 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a, 0x00,
0x16, 0x00, 0x17, 0x00, 0x08, 0x00, 0x06, 0x00,
0x07, 0x00, 0x14, 0x00, 0x15, 0x00, 0x04, 0x00,
0x05, 0x00, 0x12, 0x00, 0x13, 0x00, 0x01, 0x00,
0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10, 0x00,
0x11, 0x00, 0x23, 0x00, 0x68, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xe8, 0x4b,
0xd1, 0xef, 0xba, 0xfc, 0x00, 0xd4, 0x2f, 0xf5,
0x6f, 0xba, 0xdc, 0xb7, 0xd7, 0x87, 0x59, 0x58,
0x05, 0x06, 0x36, 0x8f, 0x47, 0xc7, 0x9e, 0x4c,
0xf8, 0xb5, 0xd7, 0x55, 0x84, 0x64, 0x0b, 0x4c,
0x0b, 0xad, 0x8d, 0x9b, 0x79, 0x4d, 0xd7, 0x61,
0xf7, 0x2b, 0x89, 0x46, 0x2b, 0x52, 0x1a, 0x3f,
0x51, 0x58, 0xce, 0x59, 0x23, 0xef, 0x60, 0x55,
0x07, 0xc0, 0x46, 0x97, 0xad, 0x0a, 0xe3, 0x55,
0x10, 0x06, 0xff, 0x57, 0x0c, 0xb1, 0x49, 0xac,
0x80, 0xc6, 0xc3, 0x95, 0x5f, 0x12, 0xe2, 0xe5,
0xaa, 0x9f, 0x78, 0xc2, 0x20, 0x00, 0x0f, 0x00,
0x01, 0x01,
},
{
0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00,
0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0xb5, 0x7a, 0x00, 0x96,
0x92, 0x51, 0xfc, 0x41, 0x16, 0x29, 0xc0, 0x54,
0x5e, 0xa7, 0xa9, 0x1f, 0xf8, 0xbf, 0x79, 0xfa,
0x49, 0x5a, 0x15, 0x28, 0x72, 0x9a, 0x59, 0xf9,
0x9b, 0xc4, 0x3a, 0xa8, 0x00, 0x05, 0x00, 0x14,
0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
0x00, 0x24, 0x2c, 0x86, 0xdd, 0x85, 0x21, 0xa7,
0xda, 0x25, 0xf5, 0x55, 0x62, 0x2d, 0x82, 0x6b,
0x9d, 0x67, 0x22, 0x28, 0xf4, 0x55, 0x33, 0xd0,
0x77, 0xc0, 0x9e, 0xb7, 0xf4, 0x96, 0x07, 0x8c,
0xf5, 0xea, 0x5b, 0x50, 0xa4, 0xb7,
},
{
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
0x01, 0x00, 0x24, 0x15, 0x14, 0x9c, 0x21, 0xdd,
0x47, 0x61, 0x52, 0xf9, 0x22, 0x15, 0x55, 0x3c,
0xbd, 0xd7, 0xff, 0xf9, 0xbd, 0x84, 0xec, 0x97,
0x2d, 0x4e, 0xa9, 0x6a, 0xb9, 0x9b, 0x96, 0xc6,
0x9e, 0x5c, 0x77, 0xa8, 0x5d, 0x7a, 0x08,
},
{
0x17, 0x03, 0x01, 0x00, 0x21, 0x04, 0xab, 0x0f,
0x7c, 0x54, 0x20, 0xab, 0x34, 0xa3, 0x73, 0x92,
0xc5, 0xaa, 0xdd, 0x5b, 0xf5, 0x0c, 0xe4, 0x4f,
0xf1, 0x93, 0x07, 0xe5, 0xe8, 0x72, 0xc2, 0x03,
0x60, 0xfa, 0x64, 0x01, 0x00, 0x25, 0x15, 0x03,
0x01, 0x00, 0x16, 0xc7, 0xd9, 0xff, 0x67, 0xfc,
0x7a, 0xac, 0x8a, 0xe6, 0x23, 0xfe, 0x32, 0xbf,
0x84, 0xe1, 0xe2, 0xf5, 0x6a, 0xc8, 0xda, 0x30,
0x8f,
}, },
} }

View File

@ -106,10 +106,9 @@ 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")
// keysFromPreMasterSecret generates the connection keys from the pre master // masterFromPreMasterSecret generates the master secret from the pre-master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in // secret. See http://tools.ietf.org/html/rfc5246#section-8.1
// RFC 2246, section 6.3. func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []byte {
func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
prf := pRF10 prf := pRF10
if version == versionSSL30 { if version == versionSSL30 {
prf = pRF30 prf = pRF30
@ -118,9 +117,21 @@ func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serv
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:]) prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
return masterSecret
}
// keysFromMasterSecret generates the connection keys from the master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, section 6.3.
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
copy(seed[0:len(clientRandom)], serverRandom) copy(seed[0:len(clientRandom)], serverRandom)
copy(seed[len(serverRandom):], clientRandom) copy(seed[len(serverRandom):], clientRandom)

View File

@ -48,18 +48,23 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
in, _ := hex.DecodeString(test.preMasterSecret) in, _ := hex.DecodeString(test.preMasterSecret)
clientRandom, _ := hex.DecodeString(test.clientRandom) clientRandom, _ := hex.DecodeString(test.clientRandom)
serverRandom, _ := hex.DecodeString(test.serverRandom) serverRandom, _ := hex.DecodeString(test.serverRandom)
master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret(test.version, in, clientRandom, serverRandom, test.macLen, test.keyLen, 0)
masterString := hex.EncodeToString(master) masterSecret := masterFromPreMasterSecret(test.version, in, clientRandom, serverRandom)
if s := hex.EncodeToString(masterSecret); s != test.masterSecret {
t.Errorf("#%d: bad master secret %s, want %s", s, test.masterSecret)
continue
}
clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromMasterSecret(test.version, masterSecret, clientRandom, serverRandom, test.macLen, test.keyLen, 0)
clientMACString := hex.EncodeToString(clientMAC) clientMACString := hex.EncodeToString(clientMAC)
serverMACString := hex.EncodeToString(serverMAC) serverMACString := hex.EncodeToString(serverMAC)
clientKeyString := hex.EncodeToString(clientKey) clientKeyString := hex.EncodeToString(clientKey)
serverKeyString := hex.EncodeToString(serverKey) serverKeyString := hex.EncodeToString(serverKey)
if masterString != test.masterSecret || if clientMACString != test.clientMAC ||
clientMACString != test.clientMAC ||
serverMACString != test.serverMAC || serverMACString != test.serverMAC ||
clientKeyString != test.clientKey || clientKeyString != test.clientKey ||
serverKeyString != test.serverKey { serverKeyString != test.serverKey {
t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s, %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverKeyString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey) t.Errorf("#%d: got: (%s, %s, %s, %s) want: (%s, %s, %s, %s)", i, clientMACString, serverMACString, clientKeyString, serverKeyString, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey)
} }
} }
} }

View File

@ -1,61 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"crypto/x509"
"runtime"
"testing"
)
var tlsServers = []string{
"google.com",
"github.com",
"twitter.com",
}
func TestOSCertBundles(t *testing.T) {
if testing.Short() {
t.Logf("skipping certificate tests in short mode")
return
}
for _, addr := range tlsServers {
conn, err := Dial("tcp", addr+":443", &Config{ServerName: addr})
if err != nil {
t.Errorf("unable to verify %v: %v", addr, err)
continue
}
err = conn.Close()
if err != nil {
t.Error(err)
}
}
}
func TestCertHostnameVerifyWindows(t *testing.T) {
if runtime.GOOS != "windows" {
return
}
if testing.Short() {
t.Logf("skipping certificate tests in short mode")
return
}
for _, addr := range tlsServers {
cfg := &Config{ServerName: "example.com"}
conn, err := Dial("tcp", addr+":443", cfg)
if err == nil {
conn.Close()
t.Errorf("should fail to verify for example.com: %v", addr)
continue
}
_, ok := err.(x509.HostnameError)
if !ok {
t.Errorf("error type mismatch, got: %v", err)
}
}
}

View File

@ -0,0 +1,182 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
"errors"
"io"
)
// sessionState contains the information that is serialized into a session
// ticket in order to later resume a connection.
type sessionState struct {
vers uint16
cipherSuite uint16
masterSecret []byte
certificates [][]byte
}
func (s *sessionState) equal(i interface{}) bool {
s1, ok := i.(*sessionState)
if !ok {
return false
}
if s.vers != s1.vers ||
s.cipherSuite != s1.cipherSuite ||
!bytes.Equal(s.masterSecret, s1.masterSecret) {
return false
}
if len(s.certificates) != len(s1.certificates) {
return false
}
for i := range s.certificates {
if !bytes.Equal(s.certificates[i], s1.certificates[i]) {
return false
}
}
return true
}
func (s *sessionState) marshal() []byte {
length := 2 + 2 + 2 + len(s.masterSecret) + 2
for _, cert := range s.certificates {
length += 4 + len(cert)
}
ret := make([]byte, length)
x := ret
x[0] = byte(s.vers >> 8)
x[1] = byte(s.vers)
x[2] = byte(s.cipherSuite >> 8)
x[3] = byte(s.cipherSuite)
x[4] = byte(len(s.masterSecret) >> 8)
x[5] = byte(len(s.masterSecret))
x = x[6:]
copy(x, s.masterSecret)
x = x[len(s.masterSecret):]
x[0] = byte(len(s.certificates) >> 8)
x[1] = byte(len(s.certificates))
x = x[2:]
for _, cert := range s.certificates {
x[0] = byte(len(cert) >> 24)
x[1] = byte(len(cert) >> 16)
x[2] = byte(len(cert) >> 8)
x[3] = byte(len(cert))
copy(x[4:], cert)
x = x[4+len(cert):]
}
return ret
}
func (s *sessionState) unmarshal(data []byte) bool {
if len(data) < 8 {
return false
}
s.vers = uint16(data[0])<<8 | uint16(data[1])
s.cipherSuite = uint16(data[2])<<8 | uint16(data[3])
masterSecretLen := int(data[4])<<8 | int(data[5])
data = data[6:]
if len(data) < masterSecretLen {
return false
}
s.masterSecret = data[:masterSecretLen]
data = data[masterSecretLen:]
if len(data) < 2 {
return false
}
numCerts := int(data[0])<<8 | int(data[1])
data = data[2:]
s.certificates = make([][]byte, numCerts)
for i := range s.certificates {
if len(data) < 4 {
return false
}
certLen := int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3])
data = data[4:]
if certLen < 0 {
return false
}
if len(data) < certLen {
return false
}
s.certificates[i] = data[:certLen]
data = data[certLen:]
}
if len(data) > 0 {
return false
}
return true
}
func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) {
serialized := state.marshal()
encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size)
iv := encrypted[:aes.BlockSize]
macBytes := encrypted[len(encrypted)-sha256.Size:]
if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
return nil, err
}
block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
if err != nil {
return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
}
cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized)
mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
mac.Write(encrypted[:len(encrypted)-sha256.Size])
mac.Sum(macBytes[:0])
return encrypted, nil
}
func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) {
if len(encrypted) < aes.BlockSize+sha256.Size {
return nil, false
}
iv := encrypted[:aes.BlockSize]
macBytes := encrypted[len(encrypted)-sha256.Size:]
mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
mac.Write(encrypted[:len(encrypted)-sha256.Size])
expected := mac.Sum(nil)
if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
return nil, false
}
block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
if err != nil {
return nil, false
}
ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
plaintext := ciphertext
cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
state := new(sessionState)
ok := state.unmarshal(plaintext)
return state, ok
}

View File

@ -146,11 +146,17 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
return return
} }
keyDERBlock, _ := pem.Decode(keyPEMBlock) var keyDERBlock *pem.Block
for {
keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
if keyDERBlock == nil { if keyDERBlock == nil {
err = errors.New("crypto/tls: failed to parse key PEM data") err = errors.New("crypto/tls: failed to parse key PEM data")
return return
} }
if keyDERBlock.Type != "CERTIFICATE" {
break
}
}
// OpenSSL 0.9.8 generates PKCS#1 private keys by default, while // OpenSSL 0.9.8 generates PKCS#1 private keys by default, while
// OpenSSL 1.0.0 generates PKCS#8 keys. We try both. // OpenSSL 1.0.0 generates PKCS#8 keys. We try both.

View File

@ -0,0 +1,47 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"testing"
)
var certPEM = `-----BEGIN CERTIFICATE-----
MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ
hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa
rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv
zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW
r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
-----END CERTIFICATE-----
`
var keyPEM = `-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
-----END RSA PRIVATE KEY-----
`
func TestX509KeyPair(t *testing.T) {
_, err := X509KeyPair([]byte(keyPEM+certPEM), []byte(keyPEM+certPEM))
if err != nil {
t.Errorf("Failed to load key followed by cert: %s", err)
}
_, err = X509KeyPair([]byte(certPEM+keyPEM), []byte(certPEM+keyPEM))
if err != nil {
t.Errorf("Failed to load cert followed by key: %s", err)
println(err.Error())
}
}

View File

@ -0,0 +1,133 @@
// Copyright 2012 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 x509
// RFC 1423 describes the encryption of PEM blocks. The algorithm used to
// generate a key from the password was derived by looking at the OpenSSL
// implementation.
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/md5"
"encoding/hex"
"encoding/pem"
"errors"
"strings"
)
// rfc1423Algos represents how to create a block cipher for a decryption mode.
type rfc1423Algo struct {
cipherFunc func([]byte) (cipher.Block, error)
keySize int
}
// deriveKey uses a key derivation function to stretch the password into a key
// with the number of bits our cipher requires. This algorithm was derived from
// the OpenSSL source.
func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
hash := md5.New()
out := make([]byte, c.keySize)
var digest []byte
for i := 0; i < len(out); i += len(digest) {
hash.Reset()
hash.Write(digest)
hash.Write(password)
hash.Write(salt)
digest = hash.Sum(digest[:0])
copy(out[i:], digest)
}
return out
}
// rfc1423Algos is a mapping of encryption algorithm to an rfc1423Algo that can
// create block ciphers for that mode.
var rfc1423Algos = map[string]rfc1423Algo{
"DES-CBC": {des.NewCipher, 8},
"DES-EDE3-CBC": {des.NewTripleDESCipher, 24},
"AES-128-CBC": {aes.NewCipher, 16},
"AES-192-CBC": {aes.NewCipher, 24},
"AES-256-CBC": {aes.NewCipher, 32},
}
// IsEncryptedPEMBlock returns if the PEM block is password encrypted.
func IsEncryptedPEMBlock(b *pem.Block) bool {
_, ok := b.Headers["DEK-Info"]
return ok
}
// IncorrectPasswordError is returned when an incorrect password is detected.
var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
// DecryptPEMBlock takes a password encrypted PEM block and the password used to
// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects
// the DEK-Info header to determine the algorithm used for decryption. If no
// DEK-Info header is present, an error is returned. If an incorrect password
// is detected an IncorrectPasswordError is returned.
func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
dek, ok := b.Headers["DEK-Info"]
if !ok {
return nil, errors.New("x509: no DEK-Info header in block")
}
idx := strings.Index(dek, ",")
if idx == -1 {
return nil, errors.New("x509: malformed DEK-Info header")
}
mode, hexIV := dek[:idx], dek[idx+1:]
iv, err := hex.DecodeString(hexIV)
if err != nil {
return nil, err
}
if len(iv) < 8 {
return nil, errors.New("x509: not enough bytes in IV")
}
ciph, ok := rfc1423Algos[mode]
if !ok {
return nil, errors.New("x509: unknown encryption mode")
}
// Based on the OpenSSL implementation. The salt is the first 8 bytes
// of the initialization vector.
key := ciph.deriveKey(password, iv[:8])
block, err := ciph.cipherFunc(key)
if err != nil {
return nil, err
}
data := make([]byte, len(b.Bytes))
dec := cipher.NewCBCDecrypter(block, iv)
dec.CryptBlocks(data, b.Bytes)
// Blocks are padded using a scheme where the last n bytes of padding are all
// equal to n. It can pad from 1 to 8 bytes inclusive. See RFC 1423.
// For example:
// [x y z 2 2]
// [x y 7 7 7 7 7 7 7]
// If we detect a bad padding, we assume it is an invalid password.
dlen := len(data)
if dlen == 0 {
return nil, errors.New("x509: invalid padding")
}
last := data[dlen-1]
if dlen < int(last) {
return nil, IncorrectPasswordError
}
if last == 0 || last > 8 {
return nil, IncorrectPasswordError
}
for _, val := range data[dlen-int(last):] {
if val != last {
return nil, IncorrectPasswordError
}
}
return data[:dlen-int(last)], nil
}

View File

@ -0,0 +1,119 @@
// Copyright 2012 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 x509
import (
"encoding/pem"
"testing"
)
func TestDecrypt(t *testing.T) {
for _, data := range testData {
block, rest := pem.Decode(data.pemData)
if len(rest) > 0 {
t.Error(data.kind, "extra data")
}
der, err := DecryptPEMBlock(block, data.password)
if err != nil {
t.Error(data.kind, err)
continue
}
if _, err := ParsePKCS1PrivateKey(der); err != nil {
t.Error(data.kind, "Invalid private key")
}
}
}
var testData = []struct {
kind string
password []byte
pemData []byte
}{
{
kind: "DES-CBC",
password: []byte("asdf"),
pemData: []byte(`
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-CBC,34F09A4FC8DE22B5
WXxy8kbZdiZvANtKvhmPBLV7eVFj2A5z6oAxvI9KGyhG0ZK0skfnt00C24vfU7m5
ICXeoqP67lzJ18xCzQfHjDaBNs53DSDT+Iz4e8QUep1xQ30+8QKX2NA2coee3nwc
6oM1cuvhNUDemBH2i3dKgMVkfaga0zQiiOq6HJyGSncCMSruQ7F9iWEfRbFcxFCx
qtHb1kirfGKEtgWTF+ynyco6+2gMXNu70L7nJcnxnV/RLFkHt7AUU1yrclxz7eZz
XOH9VfTjb52q/I8Suozq9coVQwg4tXfIoYUdT//O+mB7zJb9HI9Ps77b9TxDE6Gm
4C9brwZ3zg2vqXcwwV6QRZMtyll9rOpxkbw6NPlpfBqkc3xS51bbxivbO/Nve4KD
r12ymjFNF4stXCfJnNqKoZ50BHmEEUDu5Wb0fpVn82XrGw7CYc4iug==
-----END RSA PRIVATE KEY-----`),
},
{
kind: "DES-EDE3-CBC",
password: []byte("asdf"),
pemData: []byte(`
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,C1F4A6A03682C2C7
0JqVdBEH6iqM7drTkj+e2W/bE3LqakaiWhb9WUVonFkhyu8ca/QzebY3b5gCvAZQ
YwBvDcT/GHospKqPx+cxDHJNsUASDZws6bz8ZXWJGwZGExKzr0+Qx5fgXn44Ms3x
8g1ENFuTXtxo+KoNK0zuAMAqp66Llcds3Fjl4XR18QaD0CrVNAfOdgATWZm5GJxk
Fgx5f84nT+/ovvreG+xeOzWgvtKo0UUZVrhGOgfKLpa57adumcJ6SkUuBtEFpZFB
ldw5w7WC7d13x2LsRkwo8ZrDKgIV+Y9GNvhuCCkTzNP0V3gNeJpd201HZHR+9n3w
3z0VjR/MGqsfcy1ziEWMNOO53At3zlG6zP05aHMnMcZoVXadEK6L1gz++inSSDCq
gI0UJP4e3JVB7AkgYymYAwiYALAkoEIuanxoc50njJk=
-----END RSA PRIVATE KEY-----`),
},
{
kind: "AES-128-CBC",
password: []byte("asdf"),
pemData: []byte(`
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,D4492E793FC835CC038A728ED174F78A
EyfQSzXSjv6BaNH+NHdXRlkHdimpF9izWlugVJAPApgXrq5YldPe2aGIOFXyJ+QE
ZIG20DYqaPzJRjTEbPNZ6Es0S2JJ5yCpKxwJuDkgJZKtF39Q2i36JeGbSZQIuWJE
GZbBpf1jDH/pr0iGonuAdl2PCCZUiy+8eLsD2tyviHUkFLOB+ykYoJ5t8ngZ/B6D
33U43LLb7+9zD4y3Q9OVHqBFGyHcxCY9+9Qh4ZnFp7DTf6RY5TNEvE3s4g6aDpBs
3NbvRVvYTgs8K9EPk4K+5R+P2kD8J8KvEIGxVa1vz8QoCJ/jr7Ka2rvNgPCex5/E
080LzLHPCrXKdlr/f50yhNWq08ZxMWQFkui+FDHPDUaEELKAXV8/5PDxw80Rtybo
AVYoCVIbZXZCuCO81op8UcOgEpTtyU5Lgh3Mw5scQL0=
-----END RSA PRIVATE KEY-----`),
},
{
kind: "AES-192-CBC",
password: []byte("asdf"),
pemData: []byte(`
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-192-CBC,E2C9FB02BCA23ADE1829F8D8BC5F5369
cqVslvHqDDM6qwU6YjezCRifXmKsrgEev7ng6Qs7UmDJOpHDgJQZI9fwMFUhIyn5
FbCu1SHkLMW52Ld3CuEqMnzWMlhPrW8tFvUOrMWPYSisv7nNq88HobZEJcUNL2MM
Y15XmHW6IJwPqhKyLHpWXyOCVEh4ODND2nV15PCoi18oTa475baxSk7+1qH7GuIs
Rb7tshNTMqHbCpyo9Rn3UxeFIf9efdl8YLiMoIqc7J8E5e9VlbeQSdLMQOgDAQJG
ReUtTw8exmKsY4gsSjhkg5uiw7/ZB1Ihto0qnfQJgjGc680qGkT1d6JfvOfeYAk6
xn5RqS/h8rYAYm64KnepfC9vIujo4NqpaREDmaLdX5MJPQ+SlytITQvgUsUq3q/t
Ss85xjQEZH3hzwjQqdJvmA4hYP6SUjxYpBM+02xZ1Xw=
-----END RSA PRIVATE KEY-----`),
},
{
kind: "AES-256-CBC",
password: []byte("asdf"),
pemData: []byte(`
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,8E7ED5CD731902CE938957A886A5FFBD
4Mxr+KIzRVwoOP0wwq6caSkvW0iS+GE2h2Ov/u+n9ZTMwL83PRnmjfjzBgfRZLVf
JFPXxUK26kMNpIdssNnqGOds+DhB+oSrsNKoxgxSl5OBoYv9eJTVYm7qOyAFIsjr
DRKAcjYCmzfesr7PVTowwy0RtHmYwyXMGDlAzzZrEvaiySFFmMyKKvtoavwaFoc7
Pz3RZScwIuubzTGJ1x8EzdffYOsdCa9Mtgpp3L136+23dOd6L/qK2EG2fzrJSHs/
2XugkleBFSMKzEp9mxXKRfa++uidQvMZTFLDK9w5YjrRvMBo/l2BoZIsq0jAIE1N
sv5Z/KwlX+3MDEpPQpUwGPlGGdLnjI3UZ+cjgqBcoMiNc6HfgbBgYJSU6aDSHuCk
clCwByxWkBNgJ2GrkwNrF26v+bGJJJNR4SKouY1jQf0=
-----END RSA PRIVATE KEY-----`),
},
}

View File

@ -28,7 +28,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
return nil, err return nil, err
} }
switch { switch {
case privKey.Algo.Algorithm.Equal(oidRSA): case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey) key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
if err != nil { if err != nil {
return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())

View File

@ -0,0 +1,31 @@
// Copyright 2012 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.
// +build plan9
package x509
import "io/ioutil"
// Possible certificate files; stop after finding one.
var certFiles = []string{
"/sys/lib/tls/ca.pem",
}
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
return nil, nil
}
func initSystemRoots() {
roots := NewCertPool()
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
break
}
}
systemRoots = roots
}

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 plan9 darwin,!cgo // +build darwin,!cgo
package x509 package x509

View File

@ -98,9 +98,13 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e
// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for // checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
// use as a certificate chain for a SSL/TLS server. // use as a certificate chain for a SSL/TLS server.
func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error { func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
if err != nil {
return err
}
sslPara := &syscall.SSLExtraCertChainPolicyPara{ sslPara := &syscall.SSLExtraCertChainPolicyPara{
AuthType: syscall.AUTHTYPE_SERVER, AuthType: syscall.AUTHTYPE_SERVER,
ServerName: syscall.StringToUTF16Ptr(opts.DNSName), ServerName: servernamep,
} }
sslPara.Size = uint32(unsafe.Sizeof(*sslPara)) sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
@ -110,7 +114,7 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex
para.Size = uint32(unsafe.Sizeof(*para)) para.Size = uint32(unsafe.Sizeof(*para))
status := syscall.CertChainPolicyStatus{} status := syscall.CertChainPolicyStatus{}
err := syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status) err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
if err != nil { if err != nil {
return err return err
} }

View File

@ -27,6 +27,9 @@ const (
// TooManyIntermediates results when a path length constraint is // TooManyIntermediates results when a path length constraint is
// violated. // violated.
TooManyIntermediates TooManyIntermediates
// IncompatibleUsage results when the certificate's key usage indicates
// that it may only be used for a different purpose.
IncompatibleUsage
) )
// CertificateInvalidError results when an odd error occurs. Users of this // CertificateInvalidError results when an odd error occurs. Users of this
@ -46,6 +49,8 @@ func (e CertificateInvalidError) Error() string {
return "x509: a root or intermediate certificate is not authorized to sign in this domain" return "x509: a root or intermediate certificate is not authorized to sign in this domain"
case TooManyIntermediates: case TooManyIntermediates:
return "x509: too many intermediates for path length constraint" return "x509: too many intermediates for path length constraint"
case IncompatibleUsage:
return "x509: certificate specifies an incompatible key usage"
} }
return "x509: unknown error" return "x509: unknown error"
} }
@ -84,6 +89,11 @@ type VerifyOptions struct {
Intermediates *CertPool Intermediates *CertPool
Roots *CertPool // if nil, the system roots are used Roots *CertPool // if nil, the system roots are used
CurrentTime time.Time // if zero, the current time is used CurrentTime time.Time // if zero, the current time is used
// KeyUsage specifies which Extended Key Usage values are acceptable.
// An empty list means ExtKeyUsageServerAuth. Key usage is considered a
// constraint down the chain which mirrors Windows CryptoAPI behaviour,
// but not the spec. To accept any key usage, include ExtKeyUsageAny.
KeyUsages []ExtKeyUsage
} }
const ( const (
@ -174,7 +184,35 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
} }
} }
return c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts) candidateChains, err := c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
if err != nil {
return
}
keyUsages := opts.KeyUsages
if len(keyUsages) == 0 {
keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
}
// If any key usage is acceptable then we're done.
for _, usage := range keyUsages {
if usage == ExtKeyUsageAny {
chains = candidateChains
return
}
}
for _, candidate := range candidateChains {
if checkChainForKeyUsage(candidate, keyUsages) {
chains = append(chains, candidate)
}
}
if len(chains) == 0 {
err = CertificateInvalidError{c, IncompatibleUsage}
}
return
} }
func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate { func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate {
@ -300,3 +338,56 @@ func (c *Certificate) VerifyHostname(h string) error {
return HostnameError{c, h} return HostnameError{c, h}
} }
func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool {
usages := make([]ExtKeyUsage, len(keyUsages))
copy(usages, keyUsages)
if len(chain) == 0 {
return false
}
usagesRemaining := len(usages)
// We walk down the list and cross out any usages that aren't supported
// by each certificate. If we cross out all the usages, then the chain
// is unacceptable.
for i := len(chain) - 1; i >= 0; i-- {
cert := chain[i]
if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 {
// The certificate doesn't have any extended key usage specified.
continue
}
for _, usage := range cert.ExtKeyUsage {
if usage == ExtKeyUsageAny {
// The certificate is explicitly good for any usage.
continue
}
}
const invalidUsage ExtKeyUsage = -1
NextRequestedUsage:
for i, requestedUsage := range usages {
if requestedUsage == invalidUsage {
continue
}
for _, usage := range cert.ExtKeyUsage {
if requestedUsage == usage {
continue NextRequestedUsage
}
}
usages[i] = invalidUsage
usagesRemaining--
if usagesRemaining == 0 {
return false
}
}
}
return true
}

View File

@ -21,6 +21,7 @@ type verifyTest struct {
currentTime int64 currentTime int64
dnsName string dnsName string
systemSkip bool systemSkip bool
keyUsages []ExtKeyUsage
errorCallback func(*testing.T, int, error) bool errorCallback func(*testing.T, int, error) bool
expectedChains [][]string expectedChains [][]string
@ -113,6 +114,38 @@ var verifyTests = []verifyTest{
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"}, {"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
}, },
}, },
{
// The default configuration should reject an S/MIME chain.
leaf: smimeLeaf,
roots: []string{smimeIntermediate},
currentTime: 1339436154,
// Key usage not implemented for Windows yet.
systemSkip: true,
errorCallback: expectUsageError,
},
{
leaf: smimeLeaf,
roots: []string{smimeIntermediate},
currentTime: 1339436154,
keyUsages: []ExtKeyUsage{ExtKeyUsageServerAuth},
// Key usage not implemented for Windows yet.
systemSkip: true,
errorCallback: expectUsageError,
},
{
leaf: smimeLeaf,
roots: []string{smimeIntermediate},
currentTime: 1339436154,
keyUsages: []ExtKeyUsage{ExtKeyUsageEmailProtection},
// Key usage not implemented for Windows yet.
systemSkip: true,
expectedChains: [][]string{
{"Ryan Hurst", "GlobalSign PersonalSign 2 CA - G2"},
},
},
} }
func expectHostnameError(t *testing.T, i int, err error) (ok bool) { func expectHostnameError(t *testing.T, i int, err error) (ok bool) {
@ -131,6 +164,14 @@ func expectExpired(t *testing.T, i int, err error) (ok bool) {
return true return true
} }
func expectUsageError(t *testing.T, i int, err error) (ok bool) {
if inval, ok := err.(CertificateInvalidError); !ok || inval.Reason != IncompatibleUsage {
t.Errorf("#%d: error was not IncompatibleUsage: %s", i, err)
return false
}
return true
}
func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) { func expectAuthorityUnknown(t *testing.T, i int, err error) (ok bool) {
if _, ok := err.(UnknownAuthorityError); !ok { if _, ok := err.(UnknownAuthorityError); !ok {
t.Errorf("#%d: error was not UnknownAuthorityError: %s", i, err) t.Errorf("#%d: error was not UnknownAuthorityError: %s", i, err)
@ -157,6 +198,7 @@ func testVerify(t *testing.T, useSystemRoots bool) {
Intermediates: NewCertPool(), Intermediates: NewCertPool(),
DNSName: test.dnsName, DNSName: test.dnsName,
CurrentTime: time.Unix(test.currentTime, 0), CurrentTime: time.Unix(test.currentTime, 0),
KeyUsages: test.keyUsages,
} }
if !useSystemRoots { if !useSystemRoots {
@ -433,3 +475,58 @@ O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----` -----END CERTIFICATE-----`
const smimeLeaf = `-----BEGIN CERTIFICATE-----
MIIFBjCCA+6gAwIBAgISESFvrjT8XcJTEe6rBlPptILlMA0GCSqGSIb3DQEBBQUA
MFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYD
VQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAyIENBIC0gRzIwHhcNMTIwMTIz
MTYzNjU5WhcNMTUwMTIzMTYzNjU5WjCBlDELMAkGA1UEBhMCVVMxFjAUBgNVBAgT
DU5ldyBIYW1zcGhpcmUxEzARBgNVBAcTClBvcnRzbW91dGgxGTAXBgNVBAoTEEds
b2JhbFNpZ24sIEluYy4xEzARBgNVBAMTClJ5YW4gSHVyc3QxKDAmBgkqhkiG9w0B
CQEWGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQC4ASSTvavmsFQAob60ukSSwOAL9nT/s99ltNUCAf5fPH5j
NceMKxaQse2miOmRRIXaykcq1p/TbI70Ztce38r2mbOwqDHHPVi13GxJEyUXWgaR
BteDMu5OGyWNG1kchVsGWpbstT0Z4v0md5m1BYFnxB20ebJyOR2lXDxsFK28nnKV
+5eMj76U8BpPQ4SCH7yTMG6y0XXsB3cCrBKr2o3TOYgEKv+oNnbaoMt3UxMt9nSf
9jyIshjqfnT5Aew3CUNMatO55g5FXXdIukAweg1YSb1ls05qW3sW00T3d7dQs9/7
NuxCg/A2elmVJSoy8+MLR8JSFEf/aMgjO/TyLg/jAgMBAAGjggGPMIIBizAOBgNV
HQ8BAf8EBAMCBaAwTQYDVR0gBEYwRDBCBgorBgEEAaAyASgKMDQwMgYIKwYBBQUH
AgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMCQGA1Ud
EQQdMBuBGXJ5YW4uaHVyc3RAZ2xvYmFsc2lnbi5jb20wCQYDVR0TBAIwADAdBgNV
HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwQwYDVR0fBDwwOjA4oDagNIYyaHR0
cDovL2NybC5nbG9iYWxzaWduLmNvbS9ncy9nc3BlcnNvbmFsc2lnbjJnMi5jcmww
VQYIKwYBBQUHAQEESTBHMEUGCCsGAQUFBzAChjlodHRwOi8vc2VjdXJlLmdsb2Jh
bHNpZ24uY29tL2NhY2VydC9nc3BlcnNvbmFsc2lnbjJnMi5jcnQwHQYDVR0OBBYE
FFWiECe0/L72eVYqcWYnLV6SSjzhMB8GA1UdIwQYMBaAFD8V0m18L+cxnkMKBqiU
bCw7xe5lMA0GCSqGSIb3DQEBBQUAA4IBAQAhQi6hLPeudmf3IBF4IDzCvRI0FaYd
BKfprSk/H0PDea4vpsLbWpA0t0SaijiJYtxKjlM4bPd+2chb7ejatDdyrZIzmDVy
q4c30/xMninGKokpYA11/Ve+i2dvjulu65qasrtQRGybAuuZ67lrp/K3OMFgjV5N
C3AHYLzvNU4Dwc4QQ1BaMOg6KzYSrKbABRZajfrpC9uiePsv7mDIXLx/toBPxWNl
a5vJm5DrZdn7uHdvBCE6kMykbOLN5pmEK0UIlwKh6Qi5XD0pzlVkEZliFkBMJgub
d/eF7xeg7TKPWC5xyOFp9SdMolJM7LTC3wnSO3frBAev+q/nGs9Xxyvs
-----END CERTIFICATE-----`
const smimeIntermediate = `-----BEGIN CERTIFICATE-----
MIIEFjCCAv6gAwIBAgILBAAAAAABL07hL1IwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
MDBaFw0xOTA0MTMxMDAwMDBaMFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMSowKAYDVQQDEyFHbG9iYWxTaWduIFBlcnNvbmFsU2lnbiAy
IENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBa0H5Nez4
En3dIlFpX7e5E0YndxQ74xOBbz7kdBd+DLX0LOQMjVPU3DAgKL9ujhH+ZhHkURbH
3X/94TQSUL/z2JjsaQvS0NqyZXHhM5eeuquzOJRzEQ8+odETzHg2G0Erv7yjSeww
gkwDWDJnYUDlOjYTDUEG6+i+8Mn425reo4I0E277wD542kmVWeW7+oHv5dZo9e1Q
yWwiKTEP6BEQVVSBgThXMG4traSSDRUt3T1eQTZx5EObpiBEBO4OTqiBTJfg4vEI
YgkXzKLpnfszTB6YMDpR9/QS6p3ANB3kfAb+t6udSO3WCst0DGrwHDLBFGDR4UeY
T5KGGnI7cWL7AgMBAAGjgeUwgeIwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQI
MAYBAf8CAQAwHQYDVR0OBBYEFD8V0m18L+cxnkMKBqiUbCw7xe5lMEcGA1UdIARA
MD4wPAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWdu
LmNvbS9yZXBvc2l0b3J5LzAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmds
b2JhbHNpZ24ubmV0L3Jvb3QuY3JsMB8GA1UdIwQYMBaAFGB7ZhpFDZfKiVAvfQTN
NKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQBDc3nMpMxJMQMcYUCB3+C73UpvwDE8
eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX
eCOr7t2F/uaQKKcyqqstqLZc6vPwI/rcE9oDHugY5QEjQzIBIEaTnN6P0vege2IX
YEvTWbWwGdPytDFPYIl3/6OqNSXSnZ7DxPcdLJq2uyiga8PB/TTIIHYkdM2+1DE0
7y3rH/7TjwDVD7SLu5/SdOfKskuMPTjOEvz3K161mymW06klVhubCIWOro/Gx1Q2
2FQOZ7/2k4uYoOdBTSlb8kTAuzZNgIE0rB2BIYCTz/P6zZIKW0ogbRSH
-----END CERTIFICATE-----`

View File

@ -9,6 +9,8 @@ import (
"bytes" "bytes"
"crypto" "crypto"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
"crypto/x509/pkix" "crypto/x509/pkix"
@ -106,6 +108,8 @@ type dsaSignature struct {
R, S *big.Int R, S *big.Int
} }
type ecdsaSignature dsaSignature
type validity struct { type validity struct {
NotBefore, NotAfter time.Time NotBefore, NotAfter time.Time
} }
@ -133,6 +137,10 @@ const (
SHA512WithRSA SHA512WithRSA
DSAWithSHA1 DSAWithSHA1
DSAWithSHA256 DSAWithSHA256
ECDSAWithSHA1
ECDSAWithSHA256
ECDSAWithSHA384
ECDSAWithSHA512
) )
type PublicKeyAlgorithm int type PublicKeyAlgorithm int
@ -141,6 +149,7 @@ const (
UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
RSA RSA
DSA DSA
ECDSA
) )
// OIDs for signature algorithms // OIDs for signature algorithms
@ -160,6 +169,12 @@ const (
// dsaWithSha1 OBJECT IDENTIFIER ::= { // dsaWithSha1 OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } // iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 }
// //
// RFC 3279 2.2.3 ECDSA Signature Algorithm
//
// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) ansi-x962(10045)
// signatures(4) ecdsa-with-SHA1(1)}
//
// //
// RFC 4055 5 PKCS #1 Version 1.5 // RFC 4055 5 PKCS #1 Version 1.5
// //
@ -176,6 +191,17 @@ const (
// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101) // joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101)
// csor(3) algorithms(4) id-dsa-with-sha2(3) 2} // csor(3) algorithms(4) id-dsa-with-sha2(3) 2}
// //
// RFC 5758 3.2 ECDSA Signature Algorithm
//
// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
//
// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 }
//
// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 }
var ( var (
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
@ -185,6 +211,10 @@ var (
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
) )
func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm { func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm {
@ -205,6 +235,14 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
return DSAWithSHA1 return DSAWithSHA1
case oid.Equal(oidSignatureDSAWithSHA256): case oid.Equal(oidSignatureDSAWithSHA256):
return DSAWithSHA256 return DSAWithSHA256
case oid.Equal(oidSignatureECDSAWithSHA1):
return ECDSAWithSHA1
case oid.Equal(oidSignatureECDSAWithSHA256):
return ECDSAWithSHA256
case oid.Equal(oidSignatureECDSAWithSHA384):
return ECDSAWithSHA384
case oid.Equal(oidSignatureECDSAWithSHA512):
return ECDSAWithSHA512
} }
return UnknownSignatureAlgorithm return UnknownSignatureAlgorithm
} }
@ -218,21 +256,81 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
// //
// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840) // id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
// x9-57(10040) x9cm(4) 1 } // x9-57(10040) x9cm(4) 1 }
//
// RFC 5480, 2.1.1 Unrestricted Algorithm Identifier and Parameters
//
// id-ecPublicKey OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 }
var ( var (
oidPublicKeyRsa = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
oidPublicKeyDsa = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1} oidPublicKeyDSA = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
oidPublicKeyECDSA = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1}
) )
func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm { func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm {
switch { switch {
case oid.Equal(oidPublicKeyRsa): case oid.Equal(oidPublicKeyRSA):
return RSA return RSA
case oid.Equal(oidPublicKeyDsa): case oid.Equal(oidPublicKeyDSA):
return DSA return DSA
case oid.Equal(oidPublicKeyECDSA):
return ECDSA
} }
return UnknownPublicKeyAlgorithm return UnknownPublicKeyAlgorithm
} }
// RFC 5480, 2.1.1.1. Named Curve
//
// secp224r1 OBJECT IDENTIFIER ::= {
// iso(1) identified-organization(3) certicom(132) curve(0) 33 }
//
// secp256r1 OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
// prime(1) 7 }
//
// secp384r1 OBJECT IDENTIFIER ::= {
// iso(1) identified-organization(3) certicom(132) curve(0) 34 }
//
// secp521r1 OBJECT IDENTIFIER ::= {
// iso(1) identified-organization(3) certicom(132) curve(0) 35 }
//
// NB: secp256r1 is equivalent to prime256v1
var (
oidNamedCurveP224 = asn1.ObjectIdentifier{1, 3, 132, 0, 33}
oidNamedCurveP256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}
oidNamedCurveP384 = asn1.ObjectIdentifier{1, 3, 132, 0, 34}
oidNamedCurveP521 = asn1.ObjectIdentifier{1, 3, 132, 0, 35}
)
func namedCurveFromOID(oid asn1.ObjectIdentifier) elliptic.Curve {
switch {
case oid.Equal(oidNamedCurveP224):
return elliptic.P224()
case oid.Equal(oidNamedCurveP256):
return elliptic.P256()
case oid.Equal(oidNamedCurveP384):
return elliptic.P384()
case oid.Equal(oidNamedCurveP521):
return elliptic.P521()
}
return nil
}
func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
switch curve {
case elliptic.P224():
return oidNamedCurveP224, true
case elliptic.P256():
return oidNamedCurveP256, true
case elliptic.P384():
return oidNamedCurveP384, true
case elliptic.P521():
return oidNamedCurveP521, true
}
return nil, false
}
// KeyUsage represents the set of actions that are valid for a given key. It's // KeyUsage represents the set of actions that are valid for a given key. It's
// a bitmap of the KeyUsage* constants. // a bitmap of the KeyUsage* constants.
type KeyUsage int type KeyUsage int
@ -267,6 +365,9 @@ var (
oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2} oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3} oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4} oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
oidExtKeyUsageIPSECEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5}
oidExtKeyUsageIPSECTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6}
oidExtKeyUsageIPSECUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7}
oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8} oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9} oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
) )
@ -281,10 +382,48 @@ const (
ExtKeyUsageClientAuth ExtKeyUsageClientAuth
ExtKeyUsageCodeSigning ExtKeyUsageCodeSigning
ExtKeyUsageEmailProtection ExtKeyUsageEmailProtection
ExtKeyUsageIPSECEndSystem
ExtKeyUsageIPSECTunnel
ExtKeyUsageIPSECUser
ExtKeyUsageTimeStamping ExtKeyUsageTimeStamping
ExtKeyUsageOCSPSigning ExtKeyUsageOCSPSigning
) )
// extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID.
var extKeyUsageOIDs = []struct {
extKeyUsage ExtKeyUsage
oid asn1.ObjectIdentifier
}{
{ExtKeyUsageAny, oidExtKeyUsageAny},
{ExtKeyUsageServerAuth, oidExtKeyUsageServerAuth},
{ExtKeyUsageClientAuth, oidExtKeyUsageClientAuth},
{ExtKeyUsageCodeSigning, oidExtKeyUsageCodeSigning},
{ExtKeyUsageEmailProtection, oidExtKeyUsageEmailProtection},
{ExtKeyUsageIPSECEndSystem, oidExtKeyUsageIPSECEndSystem},
{ExtKeyUsageIPSECTunnel, oidExtKeyUsageIPSECTunnel},
{ExtKeyUsageIPSECUser, oidExtKeyUsageIPSECUser},
{ExtKeyUsageTimeStamping, oidExtKeyUsageTimeStamping},
{ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning},
}
func extKeyUsageFromOID(oid asn1.ObjectIdentifier) (eku ExtKeyUsage, ok bool) {
for _, pair := range extKeyUsageOIDs {
if oid.Equal(pair.oid) {
return pair.extKeyUsage, true
}
}
return
}
func oidFromExtKeyUsage(eku ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) {
for _, pair := range extKeyUsageOIDs {
if eku == pair.extKeyUsage {
return pair.oid, true
}
}
return
}
// A Certificate represents an X.509 certificate. // A Certificate represents an X.509 certificate.
type Certificate struct { type Certificate struct {
Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature). Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature).
@ -427,13 +566,13 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
var hashType crypto.Hash var hashType crypto.Hash
switch algo { switch algo {
case SHA1WithRSA, DSAWithSHA1: case SHA1WithRSA, DSAWithSHA1, ECDSAWithSHA1:
hashType = crypto.SHA1 hashType = crypto.SHA1
case SHA256WithRSA, DSAWithSHA256: case SHA256WithRSA, DSAWithSHA256, ECDSAWithSHA256:
hashType = crypto.SHA256 hashType = crypto.SHA256
case SHA384WithRSA: case SHA384WithRSA, ECDSAWithSHA384:
hashType = crypto.SHA384 hashType = crypto.SHA384
case SHA512WithRSA: case SHA512WithRSA, ECDSAWithSHA512:
hashType = crypto.SHA512 hashType = crypto.SHA512
default: default:
return ErrUnsupportedAlgorithm return ErrUnsupportedAlgorithm
@ -462,6 +601,18 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
return errors.New("DSA verification failure") return errors.New("DSA verification failure")
} }
return return
case *ecdsa.PublicKey:
ecdsaSig := new(ecdsaSignature)
if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
return err
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
return errors.New("crypto/x509: ECDSA signature contained zero or negative values")
}
if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
return errors.New("crypto/x509: ECDSA verification failure")
}
return
} }
return ErrUnsupportedAlgorithm return ErrUnsupportedAlgorithm
} }
@ -497,8 +648,6 @@ type nameConstraints struct {
type generalSubtree struct { type generalSubtree struct {
Name string `asn1:"tag:2,optional,ia5"` Name string `asn1:"tag:2,optional,ia5"`
Min int `asn1:"optional,tag:0"`
Max int `asn1:"optional,tag:1"`
} }
func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) { func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) {
@ -540,6 +689,27 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
Y: p, Y: p,
} }
return pub, nil return pub, nil
case ECDSA:
paramsData := keyData.Algorithm.Parameters.FullBytes
namedCurveOID := new(asn1.ObjectIdentifier)
_, err := asn1.Unmarshal(paramsData, namedCurveOID)
if err != nil {
return nil, err
}
namedCurve := namedCurveFromOID(*namedCurveOID)
if namedCurve == nil {
return nil, errors.New("crypto/x509: unsupported elliptic curve")
}
x, y := elliptic.Unmarshal(namedCurve, asn1Data)
if x == nil {
return nil, errors.New("crypto/x509: failed to unmarshal elliptic curve point")
}
pub := &ecdsa.PublicKey{
Curve: namedCurve,
X: x,
Y: y,
}
return pub, nil
default: default:
return nil, nil return nil, nil
} }
@ -694,7 +864,7 @@ func parseCertificate(in *certificate) (*Certificate, error) {
} }
for _, subtree := range constraints.Permitted { for _, subtree := range constraints.Permitted {
if subtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0 { if len(subtree.Name) == 0 {
if e.Critical { if e.Critical {
return out, UnhandledCriticalExtension{} return out, UnhandledCriticalExtension{}
} }
@ -730,22 +900,9 @@ func parseCertificate(in *certificate) (*Certificate, error) {
} }
for _, u := range keyUsage { for _, u := range keyUsage {
switch { if extKeyUsage, ok := extKeyUsageFromOID(u); ok {
case u.Equal(oidExtKeyUsageAny): out.ExtKeyUsage = append(out.ExtKeyUsage, extKeyUsage)
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageAny) } else {
case u.Equal(oidExtKeyUsageServerAuth):
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageServerAuth)
case u.Equal(oidExtKeyUsageClientAuth):
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageClientAuth)
case u.Equal(oidExtKeyUsageCodeSigning):
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageCodeSigning)
case u.Equal(oidExtKeyUsageEmailProtection):
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageEmailProtection)
case u.Equal(oidExtKeyUsageTimeStamping):
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageTimeStamping)
case u.Equal(oidExtKeyUsageOCSPSigning):
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageOCSPSigning)
default:
out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u) out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u)
} }
} }
@ -834,6 +991,7 @@ func reverseBitsInAByte(in byte) byte {
var ( var (
oidExtensionSubjectKeyId = []int{2, 5, 29, 14} oidExtensionSubjectKeyId = []int{2, 5, 29, 14}
oidExtensionKeyUsage = []int{2, 5, 29, 15} oidExtensionKeyUsage = []int{2, 5, 29, 15}
oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}
oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} oidExtensionAuthorityKeyId = []int{2, 5, 29, 35}
oidExtensionBasicConstraints = []int{2, 5, 29, 19} oidExtensionBasicConstraints = []int{2, 5, 29, 19}
oidExtensionSubjectAltName = []int{2, 5, 29, 17} oidExtensionSubjectAltName = []int{2, 5, 29, 17}
@ -842,7 +1000,7 @@ var (
) )
func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
ret = make([]pkix.Extension, 7 /* maximum number of elements. */) ret = make([]pkix.Extension, 8 /* maximum number of elements. */)
n := 0 n := 0
if template.KeyUsage != 0 { if template.KeyUsage != 0 {
@ -865,6 +1023,27 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
n++ n++
} }
if len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0 {
ret[n].Id = oidExtensionExtendedKeyUsage
var oids []asn1.ObjectIdentifier
for _, u := range template.ExtKeyUsage {
if oid, ok := oidFromExtKeyUsage(u); ok {
oids = append(oids, oid)
} else {
panic("internal error")
}
}
oids = append(oids, template.UnknownExtKeyUsage...)
ret[n].Value, err = asn1.Marshal(oids)
if err != nil {
return
}
n++
}
if template.BasicConstraintsValid { if template.BasicConstraintsValid {
ret[n].Id = oidExtensionBasicConstraints ret[n].Id = oidExtensionBasicConstraints
ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen}) ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen})
@ -941,11 +1120,6 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
return ret[0:n], nil return ret[0:n], nil
} }
var (
oidSHA1WithRSA = []int{1, 2, 840, 113549, 1, 1, 5}
oidRSA = []int{1, 2, 840, 113549, 1, 1, 1}
)
func subjectBytes(cert *Certificate) ([]byte, error) { func subjectBytes(cert *Certificate) ([]byte, error) {
if len(cert.RawSubject) > 0 { if len(cert.RawSubject) > 0 {
return cert.RawSubject, nil return cert.RawSubject, nil
@ -956,8 +1130,9 @@ func subjectBytes(cert *Certificate) ([]byte, error) {
// CreateCertificate creates a new certificate based on a template. The // CreateCertificate creates a new certificate based on a template. The
// following members of template are used: SerialNumber, Subject, NotBefore, // following members of template are used: SerialNumber, Subject, NotBefore,
// NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId, // NotAfter, KeyUsage, ExtKeyUsage, UnknownExtKeyUsage, BasicConstraintsValid,
// DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains. // IsCA, MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical,
// PermittedDNSDomains.
// //
// The certificate is signed by parent. If parent is equal to template then the // The certificate is signed by parent. If parent is equal to template then the
// certificate is self-signed. The parameter pub is the public key of the // certificate is self-signed. The parameter pub is the public key of the
@ -965,23 +1140,61 @@ func subjectBytes(cert *Certificate) ([]byte, error) {
// //
// The returned slice is the certificate in DER encoding. // The returned slice is the certificate in DER encoding.
// //
// The only supported key type is RSA (*rsa.PublicKey for pub, *rsa.PrivateKey // The only supported key types are RSA and ECDSA (*rsa.PublicKey or
// for priv). // *ecdsa.PublicKey for pub, *rsa.PrivateKey or *ecdsa.PublicKey for priv).
func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) { func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interface{}, priv interface{}) (cert []byte, err error) {
rsaPub, ok := pub.(*rsa.PublicKey) var publicKeyBytes []byte
if !ok { var publicKeyAlgorithm pkix.AlgorithmIdentifier
return nil, errors.New("x509: non-RSA public keys not supported")
}
rsaPriv, ok := priv.(*rsa.PrivateKey) switch pub := pub.(type) {
if !ok { case *rsa.PublicKey:
return nil, errors.New("x509: non-RSA private keys not supported") publicKeyBytes, err = asn1.Marshal(rsaPublicKey{
} N: pub.N,
E: pub.E,
asn1PublicKey, err := asn1.Marshal(rsaPublicKey{
N: rsaPub.N,
E: rsaPub.E,
}) })
publicKeyAlgorithm.Algorithm = oidPublicKeyRSA
case *ecdsa.PublicKey:
oid, ok := oidFromNamedCurve(pub.Curve)
if !ok {
return nil, errors.New("x509: unknown elliptic curve")
}
publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA
var paramBytes []byte
paramBytes, err = asn1.Marshal(oid)
if err != nil {
return
}
publicKeyAlgorithm.Parameters.FullBytes = paramBytes
publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
default:
return nil, errors.New("x509: only RSA and ECDSA public keys supported")
}
var signatureAlgorithm pkix.AlgorithmIdentifier
var hashFunc crypto.Hash
switch priv := priv.(type) {
case *rsa.PrivateKey:
signatureAlgorithm.Algorithm = oidSignatureSHA1WithRSA
hashFunc = crypto.SHA1
case *ecdsa.PrivateKey:
switch priv.Curve {
case elliptic.P224(), elliptic.P256():
hashFunc = crypto.SHA256
signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA256
case elliptic.P384():
hashFunc = crypto.SHA384
signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA384
case elliptic.P521():
hashFunc = crypto.SHA512
signatureAlgorithm.Algorithm = oidSignatureECDSAWithSHA512
default:
return nil, errors.New("x509: unknown elliptic curve")
}
default:
return nil, errors.New("x509: only RSA and ECDSA private keys supported")
}
if err != nil { if err != nil {
return return
} }
@ -1005,15 +1218,15 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
return return
} }
encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
c := tbsCertificate{ c := tbsCertificate{
Version: 2, Version: 2,
SerialNumber: template.SerialNumber, SerialNumber: template.SerialNumber,
SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, SignatureAlgorithm: signatureAlgorithm,
Issuer: asn1.RawValue{FullBytes: asn1Issuer}, Issuer: asn1.RawValue{FullBytes: asn1Issuer},
Validity: validity{template.NotBefore, template.NotAfter}, Validity: validity{template.NotBefore, template.NotAfter},
Subject: asn1.RawValue{FullBytes: asn1Subject}, Subject: asn1.RawValue{FullBytes: asn1Subject},
PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},
Extensions: extensions, Extensions: extensions,
} }
@ -1024,11 +1237,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
c.Raw = tbsCertContents c.Raw = tbsCertContents
h := sha1.New() h := hashFunc.New()
h.Write(tbsCertContents) h.Write(tbsCertContents)
digest := h.Sum(nil) digest := h.Sum(nil)
signature, err := rsa.SignPKCS1v15(rand, rsaPriv, crypto.SHA1, digest) var signature []byte
switch priv := priv.(type) {
case *rsa.PrivateKey:
signature, err = rsa.SignPKCS1v15(rand, priv, hashFunc, digest)
case *ecdsa.PrivateKey:
var r, s *big.Int
if r, s, err = ecdsa.Sign(rand, priv, digest); err == nil {
signature, err = asn1.Marshal(ecdsaSignature{r, s})
}
default:
panic("internal error")
}
if err != nil { if err != nil {
return return
} }
@ -1036,7 +1262,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
cert, err = asn1.Marshal(certificate{ cert, err = asn1.Marshal(certificate{
nil, nil,
c, c,
pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, signatureAlgorithm,
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
}) })
return return

View File

@ -7,14 +7,19 @@ package x509
import ( import (
"bytes" "bytes"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
_ "crypto/sha256"
_ "crypto/sha512"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/asn1" "encoding/asn1"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/pem" "encoding/pem"
"math/big" "math/big"
"reflect"
"testing" "testing"
"time" "time"
) )
@ -237,18 +242,37 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
random := rand.Reader random := rand.Reader
block, _ := pem.Decode([]byte(pemPrivateKey)) block, _ := pem.Decode([]byte(pemPrivateKey))
priv, err := ParsePKCS1PrivateKey(block.Bytes) rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
if err != nil { if err != nil {
t.Errorf("Failed to parse private key: %s", err) t.Fatalf("Failed to parse private key: %s", err)
return
} }
ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("Failed to generate ECDSA key: %s", err)
}
tests := []struct {
name string
pub, priv interface{}
checkSig bool
}{
{"RSA/RSA", &rsaPriv.PublicKey, rsaPriv, true},
{"RSA/ECDSA", &rsaPriv.PublicKey, ecdsaPriv, false},
{"ECDSA/RSA", &ecdsaPriv.PublicKey, rsaPriv, false},
{"ECDSA/ECDSA", &ecdsaPriv.PublicKey, ecdsaPriv, true},
}
testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{3, 2, 1}}
for _, test := range tests {
commonName := "test.example.com" commonName := "test.example.com"
template := Certificate{ template := Certificate{
SerialNumber: big.NewInt(1), SerialNumber: big.NewInt(1),
Subject: pkix.Name{ Subject: pkix.Name{
CommonName: commonName, CommonName: commonName,
Organization: []string{"Acme Co"}, Organization: []string{"Σ Acme Co"},
}, },
NotBefore: time.Unix(1000, 0), NotBefore: time.Unix(1000, 0),
NotAfter: time.Unix(100000, 0), NotAfter: time.Unix(100000, 0),
@ -256,6 +280,9 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
SubjectKeyId: []byte{1, 2, 3, 4}, SubjectKeyId: []byte{1, 2, 3, 4},
KeyUsage: KeyUsageCertSign, KeyUsage: KeyUsageCertSign,
ExtKeyUsage: testExtKeyUsage,
UnknownExtKeyUsage: testUnknownExtKeyUsage,
BasicConstraintsValid: true, BasicConstraintsValid: true,
IsCA: true, IsCA: true,
DNSNames: []string{"test.example.com"}, DNSNames: []string{"test.example.com"},
@ -264,38 +291,156 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
PermittedDNSDomains: []string{".example.com", "example.com"}, PermittedDNSDomains: []string{".example.com", "example.com"},
} }
derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv) derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
if err != nil { if err != nil {
t.Errorf("Failed to create certificate: %s", err) t.Errorf("%s: failed to create certificate: %s", test.name, err)
return continue
} }
cert, err := ParseCertificate(derBytes) cert, err := ParseCertificate(derBytes)
if err != nil { if err != nil {
t.Errorf("Failed to parse certificate: %s", err) t.Errorf("%s: failed to parse certificate: %s", test.name, err)
return continue
} }
if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) { if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {
t.Errorf("Failed to parse policy identifiers: got:%#v want:%#v", cert.PolicyIdentifiers, template.PolicyIdentifiers) t.Errorf("%s: failed to parse policy identifiers: got:%#v want:%#v", test.name, cert.PolicyIdentifiers, template.PolicyIdentifiers)
} }
if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" { if len(cert.PermittedDNSDomains) != 2 || cert.PermittedDNSDomains[0] != ".example.com" || cert.PermittedDNSDomains[1] != "example.com" {
t.Errorf("Failed to parse name constraints: %#v", cert.PermittedDNSDomains) t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains)
} }
if cert.Subject.CommonName != commonName { if cert.Subject.CommonName != commonName {
t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName) t.Errorf("%s: subject wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Subject.CommonName, commonName)
} }
if cert.Issuer.CommonName != commonName { if cert.Issuer.CommonName != commonName {
t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName) t.Errorf("%s: issuer wasn't correctly copied from the template. Got %s, want %s", test.name, cert.Issuer.CommonName, commonName)
} }
if !reflect.DeepEqual(cert.ExtKeyUsage, testExtKeyUsage) {
t.Errorf("%s: extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.ExtKeyUsage, testExtKeyUsage)
}
if !reflect.DeepEqual(cert.UnknownExtKeyUsage, testUnknownExtKeyUsage) {
t.Errorf("%s: unknown extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.UnknownExtKeyUsage, testUnknownExtKeyUsage)
}
if test.checkSig {
err = cert.CheckSignatureFrom(cert) err = cert.CheckSignatureFrom(cert)
if err != nil { if err != nil {
t.Errorf("Signature verification failed: %s", err) t.Errorf("%s: signature verification failed: %s", test.name, err)
return }
}
}
}
// Self-signed certificate using ECDSA with SHA1 & secp256r1
var ecdsaSHA1CertPem = `
-----BEGIN CERTIFICATE-----
MIICDjCCAbUCCQDF6SfN0nsnrjAJBgcqhkjOPQQBMIGPMQswCQYDVQQGEwJVUzET
MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMG
A1UECgwMR29vZ2xlLCBJbmMuMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIwMjAyMDUw
WhcNMjIwNTE4MjAyMDUwWjCBjzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTATBgNVBAoMDEdvb2dsZSwg
SW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAhBgkqhkiG9w0BCQEWFGdv
bGFuZy1kZXZAZ21haWwuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/Wgn
WQDo5+bz71T0327ERgd5SDDXFbXLpzIZDXTkjpe8QTEbsF+ezsQfrekrpDPC4Cd3
P9LY0tG+aI8IyVKdUjAJBgcqhkjOPQQBA0gAMEUCIGlsqMcRqWVIWTD6wXwe6Jk2
DKxL46r/FLgJYnzBEH99AiEA3fBouObsvV1R3oVkb4BQYnD4/4LeId6lAT43YvyV
a/A=
-----END CERTIFICATE-----
`
// Self-signed certificate using ECDSA with SHA256 & secp256r1
var ecdsaSHA256p256CertPem = `
-----BEGIN CERTIFICATE-----
MIICDzCCAbYCCQDlsuMWvgQzhTAKBggqhkjOPQQDAjCBjzELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
BgNVBAoMDEdvb2dsZSwgSW5jLjEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20xIzAh
BgkqhkiG9w0BCQEWFGdvbGFuZy1kZXZAZ21haWwuY29tMB4XDTEyMDUyMTAwMTkx
NloXDTIyMDUxOTAwMTkxNlowgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
Zm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYDVQQKDAxHb29nbGUs
IEluYy4xFzAVBgNVBAMMDnd3dy5nb29nbGUuY29tMSMwIQYJKoZIhvcNAQkBFhRn
b2xhbmctZGV2QGdtYWlsLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPMt
2ErhxAty5EJRu9yM+MTy+hUXm3pdW1ensAv382KoGExSXAFWP7pjJnNtHO+XSwVm
YNtqjcAGFKpweoN//kQwCgYIKoZIzj0EAwIDRwAwRAIgIYSaUA/IB81gjbIw/hUV
70twxJr5EcgOo0hLp3Jm+EYCIFDO3NNcgmURbJ1kfoS3N/0O+irUtoPw38YoNkqJ
h5wi
-----END CERTIFICATE-----
`
// Self-signed certificate using ECDSA with SHA256 & secp384r1
var ecdsaSHA256p384CertPem = `
-----BEGIN CERTIFICATE-----
MIICSjCCAdECCQDje/no7mXkVzAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMDYxMDM0
WhcNMjIwNTE5MDYxMDM0WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
YW5nLWRldkBnbWFpbC5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARRuzRNIKRK
jIktEmXanNmrTR/q/FaHXLhWRZ6nHWe26Fw7Rsrbk+VjGy4vfWtNn7xSFKrOu5ze
qxKnmE0h5E480MNgrUiRkaGO2GMJJVmxx20aqkXOk59U8yGA4CghE6MwCgYIKoZI
zj0EAwIDZwAwZAIwBZEN8gvmRmfeP/9C1PRLzODIY4JqWub2PLRT4mv9GU+yw3Gr
PU9A3CHMdEcdw/MEAjBBO1lId8KOCh9UZunsSMfqXiVurpzmhWd6VYZ/32G+M+Mh
3yILeYQzllt/g0rKVRk=
-----END CERTIFICATE-----
`
// Self-signed certificate using ECDSA with SHA384 & secp521r1
var ecdsaSHA384p521CertPem = `
-----BEGIN CERTIFICATE-----
MIICljCCAfcCCQDhp1AFD/ahKjAKBggqhkjOPQQDAzCBjjELMAkGA1UEBhMCVVMx
EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDAS
BgNVBAoMC0dvb2dsZSwgSW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEG
CSqGSIb3DQEJARYUZ29sYW5nLWRldkBnbWFpbC5jb20wHhcNMTIwNTIxMTUwNDI5
WhcNMjIwNTE5MTUwNDI5WjCBjjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlm
b3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFDASBgNVBAoMC0dvb2dsZSwg
SW5jMRcwFQYDVQQDDA53d3cuZ29vZ2xlLmNvbTEjMCEGCSqGSIb3DQEJARYUZ29s
YW5nLWRldkBnbWFpbC5jb20wgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABACqx9Rv
IssRs1LWYcNN+WffwlHw4Tv3y8/LIAA9MF1ZScIonU9nRMxt4a2uGJVCPDw6JHpz
PaYc0E9puLoE9AfKpwFr59Jkot7dBg55SKPEFkddoip/rvmN7NPAWjMBirOwjOkm
8FPthvPhGPqsu9AvgVuHu3PosWiHGNrhh379pva8MzAKBggqhkjOPQQDAwOBjAAw
gYgCQgEHNmswkUdPpHqrVxp9PvLVl+xxPuHBkT+75z9JizyxtqykHQo9Uh6SWCYH
BF9KLolo01wMt8DjoYP5Fb3j5MH7xwJCAbWZzTOp4l4DPkIvAh4LeC4VWbwPPyqh
kBg71w/iEcSY3wUKgHGcJJrObZw7wys91I5kENljqw/Samdr3ka+jBJa
-----END CERTIFICATE-----
`
var ecdsaTests = []struct {
sigAlgo SignatureAlgorithm
pemCert string
}{
{ECDSAWithSHA1, ecdsaSHA1CertPem},
{ECDSAWithSHA256, ecdsaSHA256p256CertPem},
{ECDSAWithSHA256, ecdsaSHA256p384CertPem},
{ECDSAWithSHA384, ecdsaSHA384p521CertPem},
}
func TestECDSA(t *testing.T) {
for i, test := range ecdsaTests {
pemBlock, _ := pem.Decode([]byte(test.pemCert))
cert, err := ParseCertificate(pemBlock.Bytes)
if err != nil {
t.Errorf("%d: failed to parse certificate: %s", i, err)
continue
}
if sa := cert.SignatureAlgorithm; sa != test.sigAlgo {
t.Errorf("%d: signature algorithm is %v, want %v", i, sa, test.sigAlgo)
}
if parsedKey, ok := cert.PublicKey.(*ecdsa.PublicKey); !ok {
t.Errorf("%d: wanted an ECDSA public key but found: %#v", i, parsedKey)
}
if pka := cert.PublicKeyAlgorithm; pka != ECDSA {
t.Errorf("%d: public key algorithm is %v, want ECDSA", i, pka)
}
if err = cert.CheckSignatureFrom(cert); err != nil {
t.Errorf("%d: certificate verfication failed: %s", i, err)
}
} }
} }

View File

@ -14,19 +14,61 @@ import (
"strconv" "strconv"
) )
// subsetTypeArgs takes a slice of arguments from callers of the sql // driverArgs converts arguments from callers of Stmt.Exec and
// package and converts them into a slice of the driver package's // Stmt.Query into driver Values.
// "subset types". //
func subsetTypeArgs(args []interface{}) ([]driver.Value, error) { // The statement si may be nil, if no statement is available.
out := make([]driver.Value, len(args)) func driverArgs(si driver.Stmt, args []interface{}) ([]driver.Value, error) {
dargs := make([]driver.Value, len(args))
cc, ok := si.(driver.ColumnConverter)
// Normal path, for a driver.Stmt that is not a ColumnConverter.
if !ok {
for n, arg := range args { for n, arg := range args {
var err error var err error
out[n], err = driver.DefaultParameterConverter.ConvertValue(arg) dargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
if err != nil { if err != nil {
return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err) return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
} }
} }
return out, nil return dargs, nil
}
// Let the Stmt convert its own arguments.
for n, arg := range args {
// First, see if the value itself knows how to convert
// itself to a driver type. For example, a NullString
// struct changing into a string or nil.
if svi, ok := arg.(driver.Valuer); ok {
sv, err := svi.Value()
if err != nil {
return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err)
}
if !driver.IsValue(sv) {
return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv)
}
arg = sv
}
// Second, ask the column to sanity check itself. For
// example, drivers might use this to make sure that
// an int64 values being inserted into a 16-bit
// integer field is in range (before getting
// truncated), or that a nil can't go into a NOT NULL
// column before going across the network to get the
// same error.
var err error
dargs[n], err = cc.ColumnConverter(n).ConvertValue(arg)
if err != nil {
return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n, err)
}
if !driver.IsValue(dargs[n]) {
return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
arg, dargs[n])
}
}
return dargs, nil
} }
// convertAssign copies to dest the value in src, converting it if possible. // convertAssign copies to dest the value in src, converting it if possible.

View File

@ -383,6 +383,9 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
} }
func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter { func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
if len(s.placeholderConverter) == 0 {
return driver.DefaultParameterConverter
}
return s.placeholderConverter[idx] return s.placeholderConverter[idx]
} }
@ -598,6 +601,28 @@ func (rc *rowsCursor) Next(dest []driver.Value) error {
return nil return nil
} }
// fakeDriverString is like driver.String, but indirects pointers like
// DefaultValueConverter.
//
// This could be surprising behavior to retroactively apply to
// driver.String now that Go1 is out, but this is convenient for
// our TestPointerParamsAndScans.
//
type fakeDriverString struct{}
func (fakeDriverString) ConvertValue(v interface{}) (driver.Value, error) {
switch c := v.(type) {
case string, []byte:
return v, nil
case *string:
if c == nil {
return nil, nil
}
return *c, nil
}
return fmt.Sprintf("%v", v), nil
}
func converterForType(typ string) driver.ValueConverter { func converterForType(typ string) driver.ValueConverter {
switch typ { switch typ {
case "bool": case "bool":
@ -607,9 +632,9 @@ func converterForType(typ string) driver.ValueConverter {
case "int32": case "int32":
return driver.Int32 return driver.Int32
case "string": case "string":
return driver.NotNull{Converter: driver.String} return driver.NotNull{Converter: fakeDriverString{}}
case "nullstring": case "nullstring":
return driver.Null{Converter: driver.String} return driver.Null{Converter: fakeDriverString{}}
case "int64": case "int64":
// TODO(coopernurse): add type-specific converter // TODO(coopernurse): add type-specific converter
return driver.NotNull{Converter: driver.DefaultParameterConverter} return driver.NotNull{Converter: driver.DefaultParameterConverter}

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