mirror of git://gcc.gnu.org/git/gcc.git
parent
0b7463235f
commit
4ccad563d2
|
|
@ -1251,6 +1251,7 @@ Gogo::clear_file_scope()
|
|||
this->package_->bindings()->clear_file_scope();
|
||||
|
||||
// Warn about packages which were imported but not used.
|
||||
bool quiet = saw_errors();
|
||||
for (Packages::iterator p = this->packages_.begin();
|
||||
p != this->packages_.end();
|
||||
++p)
|
||||
|
|
@ -1260,7 +1261,7 @@ Gogo::clear_file_scope()
|
|||
&& package->is_imported()
|
||||
&& !package->used()
|
||||
&& !package->uses_sink_alias()
|
||||
&& !saw_errors())
|
||||
&& !quiet)
|
||||
error_at(package->location(), "imported and not used: %s",
|
||||
Gogo::message_name(package->package_name()).c_str());
|
||||
package->clear_is_imported();
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ enum Runtime_function_type
|
|||
RFT_BOOLPTR,
|
||||
// Go type int, C type int.
|
||||
RFT_INT,
|
||||
// Go type int32, C type int32_t.
|
||||
RFT_INT32,
|
||||
// Go type int64, C type int64_t.
|
||||
RFT_INT64,
|
||||
// Go type uint64, C type uint64_t.
|
||||
|
|
@ -102,6 +104,10 @@ runtime_function_type(Runtime_function_type bft)
|
|||
t = Type::lookup_integer_type("int");
|
||||
break;
|
||||
|
||||
case RFT_INT32:
|
||||
t = Type::lookup_integer_type("int32");
|
||||
break;
|
||||
|
||||
case RFT_INT64:
|
||||
t = Type::lookup_integer_type("int64");
|
||||
break;
|
||||
|
|
@ -206,6 +212,7 @@ convert_to_runtime_function_type(Runtime_function_type bft, Expression* e,
|
|||
case RFT_BOOL:
|
||||
case RFT_BOOLPTR:
|
||||
case RFT_INT:
|
||||
case RFT_INT32:
|
||||
case RFT_INT64:
|
||||
case RFT_UINT64:
|
||||
case RFT_UINTPTR:
|
||||
|
|
|
|||
|
|
@ -148,27 +148,28 @@ DEF_GO_RUNTIME(CHANRECV2, "runtime.chanrecv2", P3(TYPE, CHAN, POINTER),
|
|||
|
||||
|
||||
// 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.
|
||||
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.
|
||||
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
|
||||
// not check whether the channel is closed.
|
||||
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
|
||||
// check whether the channel is closed.
|
||||
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.
|
||||
DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT))
|
||||
DEF_GO_RUNTIME(SELECTGO, "runtime.selectgo", P1(POINTER), R1(INT32))
|
||||
|
||||
|
||||
// Panic.
|
||||
|
|
|
|||
|
|
@ -4841,6 +4841,8 @@ Select_clauses::get_backend(Translate_context* context,
|
|||
std::vector<std::vector<Bexpression*> > cases(count);
|
||||
std::vector<Bstatement*> clauses(count);
|
||||
|
||||
Type* int32_type = Type::lookup_integer_type("int32");
|
||||
|
||||
int i = 0;
|
||||
for (Clauses::iterator p = this->clauses_.begin();
|
||||
p != this->clauses_.end();
|
||||
|
|
@ -4849,7 +4851,8 @@ Select_clauses::get_backend(Translate_context* context,
|
|||
int index = p->index();
|
||||
mpz_t ival;
|
||||
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);
|
||||
cases[i].push_back(tree_to_expr(index_expr->get_tree(context)));
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ package main
|
|||
import (
|
||||
"io/ioutil" // GCCGO_ERROR "imported and not used"
|
||||
"net/http"
|
||||
"os"
|
||||
"os" // GCCGO_ERROR "imported and not used"
|
||||
)
|
||||
|
||||
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ func BenchmarkSlowNonASCII(b *testing.B) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
os.Args = []string{os.Args[0], "-test.benchtime=0.1"}
|
||||
os.Args = []string{os.Args[0], "-test.benchtime=100ms"}
|
||||
flag.Parse()
|
||||
|
||||
|
||||
rslow := testing.Benchmark(BenchmarkSlowNonASCII)
|
||||
rfast := testing.Benchmark(BenchmarkFastNonASCII)
|
||||
tslow := rslow.NsPerOp()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
2d8bc3c94ecb
|
||||
291d9f1baf75
|
||||
|
||||
The first line of this file holds the Mercurial revision number of the
|
||||
last merge done from the master library sources.
|
||||
|
|
|
|||
|
|
@ -230,6 +230,21 @@ toolexeclibgoexp_DATA = \
|
|||
exp/types.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
|
||||
|
||||
toolexeclibgogo_DATA = \
|
||||
|
|
@ -483,6 +498,7 @@ runtime_files = \
|
|||
runtime/go-unwind.c \
|
||||
runtime/chan.c \
|
||||
runtime/cpuprof.c \
|
||||
runtime/lfstack.c \
|
||||
$(runtime_lock_files) \
|
||||
runtime/mcache.c \
|
||||
runtime/mcentral.c \
|
||||
|
|
@ -492,6 +508,8 @@ runtime_files = \
|
|||
runtime/mgc0.c \
|
||||
runtime/mheap.c \
|
||||
runtime/msize.c \
|
||||
runtime/panic.c \
|
||||
runtime/parfor.c \
|
||||
runtime/print.c \
|
||||
runtime/proc.c \
|
||||
runtime/runtime.c \
|
||||
|
|
@ -656,16 +674,16 @@ go_net_newpollserver_file = go/net/newpollserver_rtems.go
|
|||
else # !LIBGO_IS_RTEMS
|
||||
if LIBGO_IS_LINUX
|
||||
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
|
||||
if LIBGO_IS_NETBSD
|
||||
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
|
||||
# By default use select with pipes. Most systems should have
|
||||
# something better.
|
||||
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_LINUX
|
||||
endif # !LIBGO_IS_RTEMS
|
||||
|
|
@ -674,13 +692,13 @@ if LIBGO_IS_LINUX
|
|||
go_net_cgo_file = go/net/cgo_linux.go
|
||||
go_net_sock_file = go/net/sock_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
|
||||
if LIBGO_IS_IRIX
|
||||
go_net_cgo_file = go/net/cgo_linux.go
|
||||
go_net_sock_file = go/net/sock_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
|
||||
if LIBGO_IS_SOLARIS
|
||||
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_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_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
|
||||
go_net_cgo_file = go/net/cgo_bsd.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_netbsd.go
|
||||
go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
|
@ -706,8 +731,12 @@ endif
|
|||
if LIBGO_IS_LINUX
|
||||
go_net_sendfile_file = go/net/sendfile_linux.go
|
||||
else
|
||||
if LIBGO_IS_FREEBSD
|
||||
go_net_sendfile_file = go/net/sendfile_freebsd.go
|
||||
else
|
||||
go_net_sendfile_file = go/net/sendfile_stub.go
|
||||
endif
|
||||
endif
|
||||
|
||||
if LIBGO_IS_LINUX
|
||||
go_net_interface_file = go/net/interface_linux.go
|
||||
|
|
@ -725,13 +754,12 @@ go_net_files = \
|
|||
go/net/dial.go \
|
||||
go/net/dnsclient.go \
|
||||
go/net/dnsclient_unix.go \
|
||||
go/net/dnsconfig.go \
|
||||
go/net/dnsconfig_unix.go \
|
||||
go/net/dnsmsg.go \
|
||||
go/net/doc.go \
|
||||
$(go_net_newpollserver_file) \
|
||||
go/net/fd.go \
|
||||
go/net/fd_unix.go \
|
||||
$(go_net_fd_os_file) \
|
||||
go/net/file.go \
|
||||
go/net/file_unix.go \
|
||||
go/net/hosts.go \
|
||||
go/net/interface.go \
|
||||
$(go_net_interface_file) \
|
||||
|
|
@ -740,6 +768,7 @@ go_net_files = \
|
|||
go/net/iprawsock_posix.go \
|
||||
go/net/ipsock.go \
|
||||
go/net/ipsock_posix.go \
|
||||
go/net/lookup.go \
|
||||
go/net/lookup_unix.go \
|
||||
go/net/mac.go \
|
||||
go/net/net.go \
|
||||
|
|
@ -747,12 +776,12 @@ go_net_files = \
|
|||
go/net/parse.go \
|
||||
go/net/pipe.go \
|
||||
go/net/port.go \
|
||||
go/net/port_unix.go \
|
||||
$(go_net_sendfile_file) \
|
||||
go/net/sock.go \
|
||||
go/net/sock_posix.go \
|
||||
$(go_net_sock_file) \
|
||||
go/net/sockopt.go \
|
||||
go/net/sockopt_posix.go \
|
||||
$(go_net_sockopt_file) \
|
||||
go/net/sockoptip.go \
|
||||
$(go_net_sockoptip_file) \
|
||||
go/net/tcpsock.go \
|
||||
go/net/tcpsock_posix.go \
|
||||
|
|
@ -831,6 +860,7 @@ go_path_files = \
|
|||
|
||||
go_reflect_files = \
|
||||
go/reflect/deepequal.go \
|
||||
go/reflect/makefunc.go \
|
||||
go/reflect/type.go \
|
||||
go/reflect/value.go
|
||||
|
||||
|
|
@ -882,12 +912,14 @@ go_strconv_files = \
|
|||
go_strings_files = \
|
||||
go/strings/reader.go \
|
||||
go/strings/replace.go \
|
||||
go/strings/search.go \
|
||||
go/strings/strings.go
|
||||
|
||||
go_sync_files = \
|
||||
go/sync/cond.go \
|
||||
go/sync/mutex.go \
|
||||
go/sync/once.go \
|
||||
go/sync/race0.go \
|
||||
go/sync/runtime.go \
|
||||
go/sync/rwmutex.go \
|
||||
go/sync/waitgroup.go
|
||||
|
|
@ -930,11 +962,28 @@ go_unicode_files = \
|
|||
go/unicode/letter.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/common.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/reader.go \
|
||||
|
|
@ -948,6 +997,7 @@ go_compress_bzip2_files = \
|
|||
go/compress/bzip2/move_to_front.go
|
||||
|
||||
go_compress_flate_files = \
|
||||
go/compress/flate/copy.go \
|
||||
go/compress/flate/deflate.go \
|
||||
go/compress/flate/huffman_bit_writer.go \
|
||||
go/compress/flate/huffman_code.go \
|
||||
|
|
@ -979,6 +1029,7 @@ go_container_ring_files = \
|
|||
go_crypto_aes_files = \
|
||||
go/crypto/aes/block.go \
|
||||
go/crypto/aes/cipher.go \
|
||||
go/crypto/aes/cipher_generic.go \
|
||||
go/crypto/aes/const.go
|
||||
go_crypto_cipher_files = \
|
||||
go/crypto/cipher/cbc.go \
|
||||
|
|
@ -1033,9 +1084,11 @@ go_crypto_tls_files = \
|
|||
go/crypto/tls/handshake_server.go \
|
||||
go/crypto/tls/key_agreement.go \
|
||||
go/crypto/tls/prf.go \
|
||||
go/crypto/tls/ticket.go \
|
||||
go/crypto/tls/tls.go
|
||||
go_crypto_x509_files = \
|
||||
go/crypto/x509/cert_pool.go \
|
||||
go/crypto/x509/pem_decrypt.go \
|
||||
go/crypto/x509/pkcs1.go \
|
||||
go/crypto/x509/pkcs8.go \
|
||||
go/crypto/x509/root.go \
|
||||
|
|
@ -1130,8 +1183,26 @@ go_exp_html_files = \
|
|||
go/exp/html/parse.go \
|
||||
go/exp/html/render.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/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/composition.go \
|
||||
go/exp/norm/forminfo.go \
|
||||
|
|
@ -1161,6 +1232,7 @@ go_exp_utf8string_files = \
|
|||
|
||||
go_go_ast_files = \
|
||||
go/go/ast/ast.go \
|
||||
go/go/ast/commentmap.go \
|
||||
go/go/ast/filter.go \
|
||||
go/go/ast/import.go \
|
||||
go/go/ast/print.go \
|
||||
|
|
@ -1170,6 +1242,7 @@ go_go_ast_files = \
|
|||
go_go_build_files = \
|
||||
go/go/build/build.go \
|
||||
go/go/build/doc.go \
|
||||
go/go/build/read.go \
|
||||
syslist.go
|
||||
go_go_doc_files = \
|
||||
go/go/doc/comment.go \
|
||||
|
|
@ -1235,6 +1308,7 @@ go_image_jpeg_files = \
|
|||
go/image/jpeg/writer.go
|
||||
|
||||
go_image_png_files = \
|
||||
go/image/png/paeth.go \
|
||||
go/image/png/reader.go \
|
||||
go/image/png/writer.go
|
||||
|
||||
|
|
@ -1243,6 +1317,7 @@ go_index_suffixarray_files = \
|
|||
go/index/suffixarray/suffixarray.go
|
||||
|
||||
go_io_ioutil_files = \
|
||||
go/io/ioutil/blackhole.go \
|
||||
go/io/ioutil/ioutil.go \
|
||||
go/io/ioutil/tempfile.go
|
||||
|
||||
|
|
@ -1358,6 +1433,7 @@ go_path_filepath_files = \
|
|||
|
||||
go_regexp_syntax_files = \
|
||||
go/regexp/syntax/compile.go \
|
||||
go/regexp/syntax/doc.go \
|
||||
go/regexp/syntax/parse.go \
|
||||
go/regexp/syntax/perl_groups.go \
|
||||
go/regexp/syntax/prog.go \
|
||||
|
|
@ -1544,6 +1620,7 @@ go_base_syscall_files = \
|
|||
go/syscall/syscall_errno.go \
|
||||
go/syscall/libcall_support.go \
|
||||
go/syscall/libcall_posix.go \
|
||||
go/syscall/race0.go \
|
||||
go/syscall/socket.go \
|
||||
go/syscall/sockcmsg_unix.go \
|
||||
go/syscall/str.go \
|
||||
|
|
@ -1714,6 +1791,9 @@ libgo_go_objs = \
|
|||
encoding/xml.lo \
|
||||
exp/ebnf.lo \
|
||||
exp/html.lo \
|
||||
exp/html/atom.lo \
|
||||
exp/locale/collate.lo \
|
||||
exp/locale/collate/build.lo \
|
||||
exp/norm.lo \
|
||||
exp/proxy.lo \
|
||||
exp/terminal.lo \
|
||||
|
|
@ -2562,6 +2642,33 @@ exp/html/check: $(CHECK_DEPS)
|
|||
@$(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
|
||||
exp/norm.lo.dep: $(go_exp_norm_files)
|
||||
$(BUILDDEPS)
|
||||
|
|
@ -3142,6 +3249,9 @@ syscall/signame.lo: go/syscall/signame.c
|
|||
syscall/wait.lo: go/syscall/wait.c
|
||||
@$(MKDIR_P) syscall
|
||||
$(LTCOMPILE) -c -o $@ $<
|
||||
syscall/check: $(CHECK_DEPS)
|
||||
@$(CHECK)
|
||||
.PHONY: syscall/check
|
||||
|
||||
# How to build a .gox file from a .lo file.
|
||||
BUILDGOX = \
|
||||
|
|
@ -3310,8 +3420,14 @@ exp/ebnf.gox: exp/ebnf.lo
|
|||
$(BUILDGOX)
|
||||
exp/html.gox: exp/html.lo
|
||||
$(BUILDGOX)
|
||||
exp/html/atom.gox: exp/html/atom.lo
|
||||
$(BUILDGOX)
|
||||
exp/inotify.gox: exp/inotify.lo
|
||||
$(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
|
||||
$(BUILDGOX)
|
||||
exp/proxy.gox: exp/proxy.lo
|
||||
|
|
@ -3484,6 +3600,7 @@ TEST_PACKAGES = \
|
|||
strconv/check \
|
||||
strings/check \
|
||||
sync/check \
|
||||
syscall/check \
|
||||
time/check \
|
||||
unicode/check \
|
||||
archive/tar/check \
|
||||
|
|
@ -3532,10 +3649,14 @@ TEST_PACKAGES = \
|
|||
encoding/xml/check \
|
||||
exp/ebnf/check \
|
||||
exp/html/check \
|
||||
exp/html/atom/check \
|
||||
$(exp_inotify_check) \
|
||||
exp/locale/collate/check \
|
||||
exp/locale/collate/build/check \
|
||||
exp/norm/check \
|
||||
exp/proxy/check \
|
||||
exp/terminal/check \
|
||||
exp/types/check \
|
||||
exp/utf8string/check \
|
||||
html/template/check \
|
||||
go/ast/check \
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \
|
|||
"$(DESTDIR)$(toolexeclibgodebugdir)" \
|
||||
"$(DESTDIR)$(toolexeclibgoencodingdir)" \
|
||||
"$(DESTDIR)$(toolexeclibgoexpdir)" \
|
||||
"$(DESTDIR)$(toolexeclibgoexphtmldir)" \
|
||||
"$(DESTDIR)$(toolexeclibgoexplocaledir)" \
|
||||
"$(DESTDIR)$(toolexeclibgoexplocalecollatedir)" \
|
||||
"$(DESTDIR)$(toolexeclibgogodir)" \
|
||||
"$(DESTDIR)$(toolexeclibgohashdir)" \
|
||||
"$(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/csv.lo encoding/gob.lo encoding/hex.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/types.lo exp/utf8string.lo html/template.lo go/ast.lo \
|
||||
go/build.lo go/doc.lo go/parser.lo go/printer.lo go/scanner.lo \
|
||||
go/token.lo hash/adler32.lo hash/crc32.lo hash/crc64.lo \
|
||||
hash/fnv.lo net/http/cgi.lo net/http/fcgi.lo \
|
||||
net/http/httptest.lo net/http/httputil.lo net/http/pprof.lo \
|
||||
image/color.lo image/draw.lo image/gif.lo image/jpeg.lo \
|
||||
image/png.lo index/suffixarray.lo io/ioutil.lo log/syslog.lo \
|
||||
log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \
|
||||
mime/multipart.lo net/http.lo net/mail.lo net/rpc.lo \
|
||||
net/smtp.lo net/textproto.lo net/url.lo old/netchan.lo \
|
||||
old/regexp.lo old/template.lo os/exec.lo $(am__DEPENDENCIES_1) \
|
||||
os/signal.lo os/user.lo path/filepath.lo regexp/syntax.lo \
|
||||
net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \
|
||||
sync/atomic.lo sync/atomic_c.lo text/scanner.lo \
|
||||
text/tabwriter.lo text/template.lo text/template/parse.lo \
|
||||
testing/iotest.lo testing/quick.lo unicode/utf16.lo \
|
||||
unicode/utf8.lo
|
||||
exp/html.lo exp/html/atom.lo exp/locale/collate.lo \
|
||||
exp/locale/collate/build.lo exp/norm.lo exp/proxy.lo \
|
||||
exp/terminal.lo exp/types.lo exp/utf8string.lo \
|
||||
html/template.lo go/ast.lo go/build.lo go/doc.lo go/parser.lo \
|
||||
go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \
|
||||
hash/crc32.lo hash/crc64.lo hash/fnv.lo net/http/cgi.lo \
|
||||
net/http/fcgi.lo net/http/httptest.lo net/http/httputil.lo \
|
||||
net/http/pprof.lo image/color.lo image/draw.lo image/gif.lo \
|
||||
image/jpeg.lo image/png.lo index/suffixarray.lo io/ioutil.lo \
|
||||
log/syslog.lo log/syslog/syslog_c.lo math/big.lo math/cmplx.lo \
|
||||
math/rand.lo mime/multipart.lo net/http.lo net/mail.lo \
|
||||
net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \
|
||||
old/netchan.lo old/regexp.lo old/template.lo os/exec.lo \
|
||||
$(am__DEPENDENCIES_1) os/signal.lo os/user.lo path/filepath.lo \
|
||||
regexp/syntax.lo net/rpc/jsonrpc.lo runtime/debug.lo \
|
||||
runtime/pprof.lo sync/atomic.lo sync/atomic_c.lo \
|
||||
text/scanner.lo text/tabwriter.lo text/template.lo \
|
||||
text/template/parse.lo testing/iotest.lo testing/quick.lo \
|
||||
unicode/utf16.lo unicode/utf8.lo
|
||||
libgo_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \
|
||||
../libbacktrace/libbacktrace.la $(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-typedesc-equal.lo go-typestring.lo go-unsafe-new.lo \
|
||||
go-unsafe-newarray.lo go-unsafe-pointer.lo go-unwind.lo \
|
||||
chan.lo cpuprof.lo $(am__objects_1) mcache.lo mcentral.lo \
|
||||
$(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo mheap.lo \
|
||||
msize.lo print.lo proc.lo runtime.lo signal_unix.lo thread.lo \
|
||||
yield.lo $(am__objects_3) iface.lo malloc.lo map.lo mprof.lo \
|
||||
reflect.lo runtime1.lo sema.lo sigqueue.lo string.lo time.lo \
|
||||
$(am__objects_4)
|
||||
chan.lo cpuprof.lo lfstack.lo $(am__objects_1) mcache.lo \
|
||||
mcentral.lo $(am__objects_2) mfinal.lo mfixalloc.lo mgc0.lo \
|
||||
mheap.lo msize.lo panic.lo parfor.lo print.lo proc.lo \
|
||||
runtime.lo signal_unix.lo thread.lo yield.lo $(am__objects_3) \
|
||||
iface.lo malloc.lo map.lo mprof.lo reflect.lo runtime1.lo \
|
||||
sema.lo sigqueue.lo string.lo time.lo $(am__objects_4)
|
||||
am_libgo_la_OBJECTS = $(am__objects_5)
|
||||
libgo_la_OBJECTS = $(am_libgo_la_OBJECTS)
|
||||
libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
|
||||
|
|
@ -250,7 +254,9 @@ DATA = $(toolexeclibgo_DATA) $(toolexeclibgoarchive_DATA) \
|
|||
$(toolexeclibgocrypto_DATA) $(toolexeclibgocryptox509_DATA) \
|
||||
$(toolexeclibgodatabase_DATA) $(toolexeclibgodatabasesql_DATA) \
|
||||
$(toolexeclibgodebug_DATA) $(toolexeclibgoencoding_DATA) \
|
||||
$(toolexeclibgoexp_DATA) $(toolexeclibgogo_DATA) \
|
||||
$(toolexeclibgoexp_DATA) $(toolexeclibgoexphtml_DATA) \
|
||||
$(toolexeclibgoexplocale_DATA) \
|
||||
$(toolexeclibgoexplocalecollate_DATA) $(toolexeclibgogo_DATA) \
|
||||
$(toolexeclibgohash_DATA) $(toolexeclibgohtml_DATA) \
|
||||
$(toolexeclibgoimage_DATA) $(toolexeclibgoindex_DATA) \
|
||||
$(toolexeclibgoio_DATA) $(toolexeclibgolog_DATA) \
|
||||
|
|
@ -611,6 +617,18 @@ toolexeclibgoexp_DATA = \
|
|||
exp/types.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
|
||||
toolexeclibgogo_DATA = \
|
||||
go/ast.gox \
|
||||
|
|
@ -811,6 +829,7 @@ runtime_files = \
|
|||
runtime/go-unwind.c \
|
||||
runtime/chan.c \
|
||||
runtime/cpuprof.c \
|
||||
runtime/lfstack.c \
|
||||
$(runtime_lock_files) \
|
||||
runtime/mcache.c \
|
||||
runtime/mcentral.c \
|
||||
|
|
@ -820,6 +839,8 @@ runtime_files = \
|
|||
runtime/mgc0.c \
|
||||
runtime/mheap.c \
|
||||
runtime/msize.c \
|
||||
runtime/panic.c \
|
||||
runtime/parfor.c \
|
||||
runtime/print.c \
|
||||
runtime/proc.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_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_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_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.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_unix.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_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_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_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_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_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_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_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_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_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.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_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockoptip_file = go/net/sockoptip_linux.go
|
||||
@LIBGO_IS_LINUX_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go
|
||||
@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go
|
||||
@LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
|
||||
@LIBGO_IS_LINUX_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
|
||||
@LIBGO_IS_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_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
|
||||
|
|
@ -982,13 +1008,12 @@ go_net_files = \
|
|||
go/net/dial.go \
|
||||
go/net/dnsclient.go \
|
||||
go/net/dnsclient_unix.go \
|
||||
go/net/dnsconfig.go \
|
||||
go/net/dnsconfig_unix.go \
|
||||
go/net/dnsmsg.go \
|
||||
go/net/doc.go \
|
||||
$(go_net_newpollserver_file) \
|
||||
go/net/fd.go \
|
||||
go/net/fd_unix.go \
|
||||
$(go_net_fd_os_file) \
|
||||
go/net/file.go \
|
||||
go/net/file_unix.go \
|
||||
go/net/hosts.go \
|
||||
go/net/interface.go \
|
||||
$(go_net_interface_file) \
|
||||
|
|
@ -997,6 +1022,7 @@ go_net_files = \
|
|||
go/net/iprawsock_posix.go \
|
||||
go/net/ipsock.go \
|
||||
go/net/ipsock_posix.go \
|
||||
go/net/lookup.go \
|
||||
go/net/lookup_unix.go \
|
||||
go/net/mac.go \
|
||||
go/net/net.go \
|
||||
|
|
@ -1004,12 +1030,12 @@ go_net_files = \
|
|||
go/net/parse.go \
|
||||
go/net/pipe.go \
|
||||
go/net/port.go \
|
||||
go/net/port_unix.go \
|
||||
$(go_net_sendfile_file) \
|
||||
go/net/sock.go \
|
||||
go/net/sock_posix.go \
|
||||
$(go_net_sock_file) \
|
||||
go/net/sockopt.go \
|
||||
go/net/sockopt_posix.go \
|
||||
$(go_net_sockopt_file) \
|
||||
go/net/sockoptip.go \
|
||||
$(go_net_sockoptip_file) \
|
||||
go/net/tcpsock.go \
|
||||
go/net/tcpsock_posix.go \
|
||||
|
|
@ -1058,6 +1084,7 @@ go_path_files = \
|
|||
|
||||
go_reflect_files = \
|
||||
go/reflect/deepequal.go \
|
||||
go/reflect/makefunc.go \
|
||||
go/reflect/type.go \
|
||||
go/reflect/value.go
|
||||
|
||||
|
|
@ -1098,12 +1125,14 @@ go_strconv_files = \
|
|||
go_strings_files = \
|
||||
go/strings/reader.go \
|
||||
go/strings/replace.go \
|
||||
go/strings/search.go \
|
||||
go/strings/strings.go
|
||||
|
||||
go_sync_files = \
|
||||
go/sync/cond.go \
|
||||
go/sync/mutex.go \
|
||||
go/sync/once.go \
|
||||
go/sync/race0.go \
|
||||
go/sync/runtime.go \
|
||||
go/sync/rwmutex.go \
|
||||
go/sync/waitgroup.go
|
||||
|
|
@ -1140,10 +1169,17 @@ go_unicode_files = \
|
|||
go/unicode/letter.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/common.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/reader.go \
|
||||
|
|
@ -1157,6 +1193,7 @@ go_compress_bzip2_files = \
|
|||
go/compress/bzip2/move_to_front.go
|
||||
|
||||
go_compress_flate_files = \
|
||||
go/compress/flate/copy.go \
|
||||
go/compress/flate/deflate.go \
|
||||
go/compress/flate/huffman_bit_writer.go \
|
||||
go/compress/flate/huffman_code.go \
|
||||
|
|
@ -1188,6 +1225,7 @@ go_container_ring_files = \
|
|||
go_crypto_aes_files = \
|
||||
go/crypto/aes/block.go \
|
||||
go/crypto/aes/cipher.go \
|
||||
go/crypto/aes/cipher_generic.go \
|
||||
go/crypto/aes/const.go
|
||||
|
||||
go_crypto_cipher_files = \
|
||||
|
|
@ -1257,10 +1295,12 @@ go_crypto_tls_files = \
|
|||
go/crypto/tls/handshake_server.go \
|
||||
go/crypto/tls/key_agreement.go \
|
||||
go/crypto/tls/prf.go \
|
||||
go/crypto/tls/ticket.go \
|
||||
go/crypto/tls/tls.go
|
||||
|
||||
go_crypto_x509_files = \
|
||||
go/crypto/x509/cert_pool.go \
|
||||
go/crypto/x509/pem_decrypt.go \
|
||||
go/crypto/x509/pkcs1.go \
|
||||
go/crypto/x509/pkcs8.go \
|
||||
go/crypto/x509/root.go \
|
||||
|
|
@ -1371,9 +1411,30 @@ go_exp_html_files = \
|
|||
go/exp/html/render.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/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/composition.go \
|
||||
go/exp/norm/forminfo.go \
|
||||
|
|
@ -1407,6 +1468,7 @@ go_exp_utf8string_files = \
|
|||
|
||||
go_go_ast_files = \
|
||||
go/go/ast/ast.go \
|
||||
go/go/ast/commentmap.go \
|
||||
go/go/ast/filter.go \
|
||||
go/go/ast/import.go \
|
||||
go/go/ast/print.go \
|
||||
|
|
@ -1417,6 +1479,7 @@ go_go_ast_files = \
|
|||
go_go_build_files = \
|
||||
go/go/build/build.go \
|
||||
go/go/build/doc.go \
|
||||
go/go/build/read.go \
|
||||
syslist.go
|
||||
|
||||
go_go_doc_files = \
|
||||
|
|
@ -1490,6 +1553,7 @@ go_image_jpeg_files = \
|
|||
go/image/jpeg/writer.go
|
||||
|
||||
go_image_png_files = \
|
||||
go/image/png/paeth.go \
|
||||
go/image/png/reader.go \
|
||||
go/image/png/writer.go
|
||||
|
||||
|
|
@ -1498,6 +1562,7 @@ go_index_suffixarray_files = \
|
|||
go/index/suffixarray/suffixarray.go
|
||||
|
||||
go_io_ioutil_files = \
|
||||
go/io/ioutil/blackhole.go \
|
||||
go/io/ioutil/ioutil.go \
|
||||
go/io/ioutil/tempfile.go
|
||||
|
||||
|
|
@ -1624,6 +1689,7 @@ go_path_filepath_files = \
|
|||
|
||||
go_regexp_syntax_files = \
|
||||
go/regexp/syntax/compile.go \
|
||||
go/regexp/syntax/doc.go \
|
||||
go/regexp/syntax/parse.go \
|
||||
go/regexp/syntax/perl_groups.go \
|
||||
go/regexp/syntax/prog.go \
|
||||
|
|
@ -1750,6 +1816,7 @@ go_base_syscall_files = \
|
|||
go/syscall/syscall_errno.go \
|
||||
go/syscall/libcall_support.go \
|
||||
go/syscall/libcall_posix.go \
|
||||
go/syscall/race0.go \
|
||||
go/syscall/socket.go \
|
||||
go/syscall/sockcmsg_unix.go \
|
||||
go/syscall/str.go \
|
||||
|
|
@ -1867,6 +1934,9 @@ libgo_go_objs = \
|
|||
encoding/xml.lo \
|
||||
exp/ebnf.lo \
|
||||
exp/html.lo \
|
||||
exp/html/atom.lo \
|
||||
exp/locale/collate.lo \
|
||||
exp/locale/collate/build.lo \
|
||||
exp/norm.lo \
|
||||
exp/proxy.lo \
|
||||
exp/terminal.lo \
|
||||
|
|
@ -2065,6 +2135,7 @@ TEST_PACKAGES = \
|
|||
strconv/check \
|
||||
strings/check \
|
||||
sync/check \
|
||||
syscall/check \
|
||||
time/check \
|
||||
unicode/check \
|
||||
archive/tar/check \
|
||||
|
|
@ -2113,10 +2184,14 @@ TEST_PACKAGES = \
|
|||
encoding/xml/check \
|
||||
exp/ebnf/check \
|
||||
exp/html/check \
|
||||
exp/html/atom/check \
|
||||
$(exp_inotify_check) \
|
||||
exp/locale/collate/check \
|
||||
exp/locale/collate/build/check \
|
||||
exp/norm/check \
|
||||
exp/proxy/check \
|
||||
exp/terminal/check \
|
||||
exp/types/check \
|
||||
exp/utf8string/check \
|
||||
html/template/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-unwind.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_sema.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)/mprof.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)/proc.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@
|
||||
@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
|
||||
@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
|
||||
|
|
@ -2988,6 +3073,20 @@ msize.lo: runtime/msize.c
|
|||
@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
|
||||
|
||||
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
|
||||
@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
|
||||
|
|
@ -3317,6 +3416,66 @@ uninstall-toolexeclibgoexpDATA:
|
|||
test -n "$$files" || exit 0; \
|
||||
echo " ( 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)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(toolexeclibgogodir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgogodir)"
|
||||
|
|
@ -3898,7 +4057,7 @@ all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) all-multi $(DATA) \
|
|||
config.h
|
||||
installdirs: installdirs-recursive
|
||||
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"; \
|
||||
done
|
||||
install: install-recursive
|
||||
|
|
@ -3969,6 +4128,9 @@ install-exec-am: install-multi install-toolexeclibLIBRARIES \
|
|||
install-toolexeclibgodatabasesqlDATA \
|
||||
install-toolexeclibgodebugDATA \
|
||||
install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
|
||||
install-toolexeclibgoexphtmlDATA \
|
||||
install-toolexeclibgoexplocaleDATA \
|
||||
install-toolexeclibgoexplocalecollateDATA \
|
||||
install-toolexeclibgogoDATA install-toolexeclibgohashDATA \
|
||||
install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \
|
||||
install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \
|
||||
|
|
@ -4034,8 +4196,11 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
|
|||
uninstall-toolexeclibgodatabasesqlDATA \
|
||||
uninstall-toolexeclibgodebugDATA \
|
||||
uninstall-toolexeclibgoencodingDATA \
|
||||
uninstall-toolexeclibgoexpDATA uninstall-toolexeclibgogoDATA \
|
||||
uninstall-toolexeclibgohashDATA \
|
||||
uninstall-toolexeclibgoexpDATA \
|
||||
uninstall-toolexeclibgoexphtmlDATA \
|
||||
uninstall-toolexeclibgoexplocaleDATA \
|
||||
uninstall-toolexeclibgoexplocalecollateDATA \
|
||||
uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
|
||||
uninstall-toolexeclibgohtmlDATA \
|
||||
uninstall-toolexeclibgoimageDATA \
|
||||
uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \
|
||||
|
|
@ -4080,6 +4245,9 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
|
|||
install-toolexeclibgodatabasesqlDATA \
|
||||
install-toolexeclibgodebugDATA \
|
||||
install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \
|
||||
install-toolexeclibgoexphtmlDATA \
|
||||
install-toolexeclibgoexplocaleDATA \
|
||||
install-toolexeclibgoexplocalecollateDATA \
|
||||
install-toolexeclibgogoDATA install-toolexeclibgohashDATA \
|
||||
install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \
|
||||
install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \
|
||||
|
|
@ -4109,8 +4277,11 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \
|
|||
uninstall-toolexeclibgodatabasesqlDATA \
|
||||
uninstall-toolexeclibgodebugDATA \
|
||||
uninstall-toolexeclibgoencodingDATA \
|
||||
uninstall-toolexeclibgoexpDATA uninstall-toolexeclibgogoDATA \
|
||||
uninstall-toolexeclibgohashDATA \
|
||||
uninstall-toolexeclibgoexpDATA \
|
||||
uninstall-toolexeclibgoexphtmlDATA \
|
||||
uninstall-toolexeclibgoexplocaleDATA \
|
||||
uninstall-toolexeclibgoexplocalecollateDATA \
|
||||
uninstall-toolexeclibgogoDATA uninstall-toolexeclibgohashDATA \
|
||||
uninstall-toolexeclibgohtmlDATA \
|
||||
uninstall-toolexeclibgoimageDATA \
|
||||
uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \
|
||||
|
|
@ -4909,6 +5080,33 @@ exp/html/check: $(CHECK_DEPS)
|
|||
@$(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
|
||||
exp/norm.lo.dep: $(go_exp_norm_files)
|
||||
$(BUILDDEPS)
|
||||
|
|
@ -5486,6 +5684,9 @@ syscall/signame.lo: go/syscall/signame.c
|
|||
syscall/wait.lo: go/syscall/wait.c
|
||||
@$(MKDIR_P) syscall
|
||||
$(LTCOMPILE) -c -o $@ $<
|
||||
syscall/check: $(CHECK_DEPS)
|
||||
@$(CHECK)
|
||||
.PHONY: syscall/check
|
||||
|
||||
bufio.gox: bufio.lo
|
||||
$(BUILDGOX)
|
||||
|
|
@ -5649,8 +5850,14 @@ exp/ebnf.gox: exp/ebnf.lo
|
|||
$(BUILDGOX)
|
||||
exp/html.gox: exp/html.lo
|
||||
$(BUILDGOX)
|
||||
exp/html/atom.gox: exp/html/atom.lo
|
||||
$(BUILDGOX)
|
||||
exp/inotify.gox: exp/inotify.lo
|
||||
$(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
|
||||
$(BUILDGOX)
|
||||
exp/proxy.gox: exp/proxy.lo
|
||||
|
|
|
|||
|
|
@ -658,6 +658,8 @@ LIBGO_IS_SOLARIS_FALSE
|
|||
LIBGO_IS_SOLARIS_TRUE
|
||||
LIBGO_IS_RTEMS_FALSE
|
||||
LIBGO_IS_RTEMS_TRUE
|
||||
LIBGO_IS_OPENBSD_FALSE
|
||||
LIBGO_IS_OPENBSD_TRUE
|
||||
LIBGO_IS_NETBSD_FALSE
|
||||
LIBGO_IS_NETBSD_TRUE
|
||||
LIBGO_IS_LINUX_FALSE
|
||||
|
|
@ -11100,7 +11102,7 @@ else
|
|||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 11103 "configure"
|
||||
#line 11105 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
|
|
@ -11206,7 +11208,7 @@ else
|
|||
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
|
||||
lt_status=$lt_dlunknown
|
||||
cat > conftest.$ac_ext <<_LT_EOF
|
||||
#line 11209 "configure"
|
||||
#line 11211 "configure"
|
||||
#include "confdefs.h"
|
||||
|
||||
#if HAVE_DLFCN_H
|
||||
|
|
@ -13471,6 +13473,7 @@ is_freebsd=no
|
|||
is_irix=no
|
||||
is_linux=no
|
||||
is_netbsd=no
|
||||
is_openbsd=no
|
||||
is_rtems=no
|
||||
is_solaris=no
|
||||
GOOS=unknown
|
||||
|
|
@ -13480,6 +13483,7 @@ case ${host} in
|
|||
*-*-irix6*) is_irix=yes; GOOS=irix ;;
|
||||
*-*-linux*) is_linux=yes; GOOS=linux ;;
|
||||
*-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;;
|
||||
*-*-openbsd*) is_openbsd=yes; GOOS=openbsd ;;
|
||||
*-*-rtems*) is_rtems=yes; GOOS=rtems ;;
|
||||
*-*-solaris2*) is_solaris=yes; GOOS=solaris ;;
|
||||
esac
|
||||
|
|
@ -13523,6 +13527,14 @@ else
|
|||
LIBGO_IS_NETBSD_FALSE=
|
||||
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
|
||||
LIBGO_IS_RTEMS_TRUE=
|
||||
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.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
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
|
||||
as_fn_error "conditional \"LIBGO_IS_RTEMS\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ is_freebsd=no
|
|||
is_irix=no
|
||||
is_linux=no
|
||||
is_netbsd=no
|
||||
is_openbsd=no
|
||||
is_rtems=no
|
||||
is_solaris=no
|
||||
GOOS=unknown
|
||||
|
|
@ -138,6 +139,7 @@ case ${host} in
|
|||
*-*-irix6*) is_irix=yes; GOOS=irix ;;
|
||||
*-*-linux*) is_linux=yes; GOOS=linux ;;
|
||||
*-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;;
|
||||
*-*-openbsd*) is_openbsd=yes; GOOS=openbsd ;;
|
||||
*-*-rtems*) is_rtems=yes; GOOS=rtems ;;
|
||||
*-*-solaris2*) is_solaris=yes; GOOS=solaris ;;
|
||||
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_LINUX, test $is_linux = 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_SOLARIS, test $is_solaris = yes)
|
||||
AC_SUBST(GOOS)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,12 @@
|
|||
// http://www.gnu.org/software/tar/manual/html_node/Standard.html
|
||||
package tar
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
blockSize = 512
|
||||
|
|
@ -49,6 +54,62 @@ type Header struct {
|
|||
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)
|
||||
|
||||
// POSIX specifies a sum of the unsigned byte values, but the Sun tar uses signed byte values.
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 }
|
||||
|
|
@ -27,7 +27,7 @@ var (
|
|||
//
|
||||
// Example:
|
||||
// tw := tar.NewWriter(w)
|
||||
// hdr := new(Header)
|
||||
// hdr := new(tar.Header)
|
||||
// hdr.Size = length of data in bytes
|
||||
// // populate other hdr fields as desired
|
||||
// if err := tw.WriteHeader(hdr); err != nil {
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
|
|||
}
|
||||
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
|
||||
// the wrong number of directory entries.
|
||||
return err
|
||||
|
|
@ -123,7 +123,7 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
size := int64(f.CompressedSize)
|
||||
size := int64(f.CompressedSize64)
|
||||
r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
|
||||
switch f.Method {
|
||||
case Store: // (no compression)
|
||||
|
|
@ -220,6 +220,8 @@ func readDirectoryHeader(f *File, r io.Reader) error {
|
|||
f.CRC32 = b.uint32()
|
||||
f.CompressedSize = b.uint32()
|
||||
f.UncompressedSize = b.uint32()
|
||||
f.CompressedSize64 = uint64(f.CompressedSize)
|
||||
f.UncompressedSize64 = uint64(f.UncompressedSize)
|
||||
filenameLen := int(b.uint16())
|
||||
extraLen := 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.Extra = d[filenameLen : 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
|
||||
}
|
||||
|
||||
|
|
@ -263,15 +287,23 @@ func readDataDescriptor(r io.Reader, f *File) error {
|
|||
return err
|
||||
}
|
||||
b := readBuf(buf[:12])
|
||||
f.CRC32 = b.uint32()
|
||||
f.CompressedSize = b.uint32()
|
||||
f.UncompressedSize = b.uint32()
|
||||
if b.uint32() != f.CRC32 {
|
||||
return ErrChecksum
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error) {
|
||||
// look for directoryEndSignature in the last 1k, then in the last 65k
|
||||
var buf []byte
|
||||
var directoryEndOffset int64
|
||||
for i, bLen := range []int64{1024, 65 * 1024} {
|
||||
if 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 {
|
||||
buf = buf[p:]
|
||||
directoryEndOffset = size - bLen + int64(p)
|
||||
break
|
||||
}
|
||||
if i == 1 || bLen == size {
|
||||
|
|
@ -292,12 +325,12 @@ func readDirectoryEnd(r io.ReaderAt, size int64) (dir *directoryEnd, err error)
|
|||
// read header into struct
|
||||
b := readBuf(buf[4:]) // skip signature
|
||||
d := &directoryEnd{
|
||||
diskNbr: b.uint16(),
|
||||
dirDiskNbr: b.uint16(),
|
||||
dirRecordsThisDisk: b.uint16(),
|
||||
directoryRecords: b.uint16(),
|
||||
directorySize: b.uint32(),
|
||||
directoryOffset: b.uint32(),
|
||||
diskNbr: uint32(b.uint16()),
|
||||
dirDiskNbr: uint32(b.uint16()),
|
||||
dirRecordsThisDisk: uint64(b.uint16()),
|
||||
directoryRecords: uint64(b.uint16()),
|
||||
directorySize: uint64(b.uint32()),
|
||||
directoryOffset: uint64(b.uint32()),
|
||||
commentLen: b.uint16(),
|
||||
}
|
||||
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")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
for i := len(b) - directoryEndLen; i >= 0; i-- {
|
||||
// defined from directoryEndSignature in struct.go
|
||||
|
|
@ -335,3 +421,9 @@ func (b *readBuf) uint32() uint32 {
|
|||
*b = (*b)[4:]
|
||||
return v
|
||||
}
|
||||
|
||||
func (b *readBuf) uint64() uint64 {
|
||||
v := binary.LittleEndian.Uint64(*b)
|
||||
*b = (*b)[8:]
|
||||
return v
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -7,12 +7,19 @@ Package zip provides support for reading and writing ZIP archives.
|
|||
|
||||
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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -27,11 +34,16 @@ const (
|
|||
fileHeaderSignature = 0x04034b50
|
||||
directoryHeaderSignature = 0x02014b50
|
||||
directoryEndSignature = 0x06054b50
|
||||
directory64LocSignature = 0x07064b50
|
||||
directory64EndSignature = 0x06064b50
|
||||
dataDescriptorSignature = 0x08074b50 // de-facto standard; required by OS X Finder
|
||||
fileHeaderLen = 30 // + filename + extra
|
||||
directoryHeaderLen = 46 // + filename + extra + comment
|
||||
directoryEndLen = 22 // + comment
|
||||
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
|
||||
creatorFAT = 0
|
||||
|
|
@ -39,22 +51,35 @@ const (
|
|||
creatorNTFS = 11
|
||||
creatorVFAT = 14
|
||||
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 {
|
||||
Name string
|
||||
CreatorVersion uint16
|
||||
ReaderVersion uint16
|
||||
Flags uint16
|
||||
Method uint16
|
||||
ModifiedTime uint16 // MS-DOS time
|
||||
ModifiedDate uint16 // MS-DOS date
|
||||
CRC32 uint32
|
||||
CompressedSize uint32
|
||||
UncompressedSize uint32
|
||||
Extra []byte
|
||||
ExternalAttrs uint32 // Meaning depends on CreatorVersion
|
||||
Comment string
|
||||
Name string
|
||||
CreatorVersion uint16
|
||||
ReaderVersion uint16
|
||||
Flags uint16
|
||||
Method uint16
|
||||
ModifiedTime uint16 // MS-DOS time
|
||||
ModifiedDate uint16 // MS-DOS date
|
||||
CRC32 uint32
|
||||
CompressedSize uint32 // deprecated; use CompressedSize64
|
||||
UncompressedSize uint32 // deprecated; use UncompressedSize64
|
||||
CompressedSize64 uint64
|
||||
UncompressedSize64 uint64
|
||||
Extra []byte
|
||||
ExternalAttrs uint32 // Meaning depends on CreatorVersion
|
||||
Comment string
|
||||
}
|
||||
|
||||
// FileInfo returns an os.FileInfo for the FileHeader.
|
||||
|
|
@ -67,8 +92,13 @@ type headerFileInfo struct {
|
|||
fh *FileHeader
|
||||
}
|
||||
|
||||
func (fi headerFileInfo) Name() string { return fi.fh.Name }
|
||||
func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) }
|
||||
func (fi headerFileInfo) Name() string { return fi.fh.Name }
|
||||
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) ModTime() time.Time { return fi.fh.ModTime() }
|
||||
func (fi headerFileInfo) Mode() os.FileMode { return fi.fh.Mode() }
|
||||
|
|
@ -78,25 +108,27 @@ func (fi headerFileInfo) Sys() interface{} { return fi.fh }
|
|||
// os.FileInfo.
|
||||
func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) {
|
||||
size := fi.Size()
|
||||
if size > (1<<32 - 1) {
|
||||
return nil, errors.New("zip: file over 4GB")
|
||||
}
|
||||
fh := &FileHeader{
|
||||
Name: fi.Name(),
|
||||
UncompressedSize: uint32(size),
|
||||
Name: fi.Name(),
|
||||
UncompressedSize64: uint64(size),
|
||||
}
|
||||
fh.SetModTime(fi.ModTime())
|
||||
fh.SetMode(fi.Mode())
|
||||
if fh.UncompressedSize64 > uint32max {
|
||||
fh.UncompressedSize = uint32max
|
||||
} else {
|
||||
fh.UncompressedSize = uint32(fh.UncompressedSize64)
|
||||
}
|
||||
return fh, nil
|
||||
}
|
||||
|
||||
type directoryEnd struct {
|
||||
diskNbr uint16 // unused
|
||||
dirDiskNbr uint16 // unused
|
||||
dirRecordsThisDisk uint16 // unused
|
||||
directoryRecords uint16
|
||||
directorySize uint32
|
||||
directoryOffset uint32 // relative to file
|
||||
diskNbr uint32 // unused
|
||||
dirDiskNbr uint32 // unused
|
||||
dirRecordsThisDisk uint64 // unused
|
||||
directoryRecords uint64
|
||||
directorySize uint64
|
||||
directoryOffset uint64 // relative to file
|
||||
commentLen uint16
|
||||
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) {
|
||||
if m&msdosDir != 0 {
|
||||
mode = os.ModeDir | 0777
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -27,7 +27,7 @@ type Writer struct {
|
|||
|
||||
type header struct {
|
||||
*FileHeader
|
||||
offset uint32
|
||||
offset uint64
|
||||
}
|
||||
|
||||
// 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.ModifiedDate)
|
||||
b.uint32(h.CRC32)
|
||||
b.uint32(h.CompressedSize)
|
||||
b.uint32(h.UncompressedSize)
|
||||
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.UncompressedSize)
|
||||
}
|
||||
b.uint16(uint16(len(h.Name)))
|
||||
b.uint16(uint16(len(h.Extra)))
|
||||
b.uint16(uint16(len(h.Comment)))
|
||||
b = b[4:] // skip disk number start and internal file attr (2x uint16)
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
@ -85,15 +107,52 @@ func (w *Writer) Close() error {
|
|||
}
|
||||
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
|
||||
var buf [directoryEndLen]byte
|
||||
b := writeBuf(buf[:])
|
||||
b.uint32(uint32(directoryEndSignature))
|
||||
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(len(w.dir))) // number of entries total
|
||||
b.uint32(uint32(end - start)) // size of directory
|
||||
b.uint32(uint32(start)) // start of directory
|
||||
b = b[4:] // skip over disk number and first disk number (2x uint16)
|
||||
b.uint16(uint16(records)) // number of entries this disk
|
||||
b.uint16(uint16(records)) // number of entries total
|
||||
b.uint32(uint32(size)) // size of directory
|
||||
b.uint32(uint32(offset)) // start of directory
|
||||
// skipped size of comment (always zero)
|
||||
if _, err := w.cw.Write(buf[:]); err != nil {
|
||||
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.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14
|
||||
fh.ReaderVersion = 0x14
|
||||
|
||||
fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte
|
||||
fh.ReaderVersion = zipVersion20
|
||||
|
||||
fw := &fileWriter{
|
||||
zipw: w.cw,
|
||||
|
|
@ -151,7 +211,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
|
|||
|
||||
h := &header{
|
||||
FileHeader: fh,
|
||||
offset: uint32(w.cw.count),
|
||||
offset: uint64(w.cw.count),
|
||||
}
|
||||
w.dir = append(w.dir, h)
|
||||
fw.header = h
|
||||
|
|
@ -173,9 +233,9 @@ func writeHeader(w io.Writer, h *FileHeader) error {
|
|||
b.uint16(h.Method)
|
||||
b.uint16(h.ModifiedTime)
|
||||
b.uint16(h.ModifiedDate)
|
||||
b.uint32(h.CRC32)
|
||||
b.uint32(h.CompressedSize)
|
||||
b.uint32(h.UncompressedSize)
|
||||
b.uint32(0) // since we are writing a data descriptor crc32,
|
||||
b.uint32(0) // compressed size,
|
||||
b.uint32(0) // and uncompressed size should be zero
|
||||
b.uint16(uint16(len(h.Name)))
|
||||
b.uint16(uint16(len(h.Extra)))
|
||||
if _, err := w.Write(buf[:]); err != nil {
|
||||
|
|
@ -218,17 +278,40 @@ func (w *fileWriter) close() error {
|
|||
// update FileHeader
|
||||
fh := w.header.FileHeader
|
||||
fh.CRC32 = w.crc32.Sum32()
|
||||
fh.CompressedSize = uint32(w.compCount.count)
|
||||
fh.UncompressedSize = uint32(w.rawCount.count)
|
||||
fh.CompressedSize64 = uint64(w.compCount.count)
|
||||
fh.UncompressedSize64 = uint64(w.rawCount.count)
|
||||
|
||||
// write data descriptor
|
||||
var buf [dataDescriptorLen]byte
|
||||
b := writeBuf(buf[:])
|
||||
if fh.isZip64() {
|
||||
fh.CompressedSize = uint32max
|
||||
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(fh.CRC32)
|
||||
b.uint32(fh.CompressedSize)
|
||||
b.uint32(fh.UncompressedSize)
|
||||
_, err := w.zipw.Write(buf[:])
|
||||
if fh.isZip64() {
|
||||
b.uint64(fh.CompressedSize64)
|
||||
b.uint64(fh.UncompressedSize64)
|
||||
} else {
|
||||
b.uint32(fh.CompressedSize)
|
||||
b.uint32(fh.UncompressedSize)
|
||||
}
|
||||
_, err := w.zipw.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -262,3 +345,8 @@ func (b *writeBuf) uint32(v uint32) {
|
|||
binary.LittleEndian.PutUint32(*b, v)
|
||||
*b = (*b)[4:]
|
||||
}
|
||||
|
||||
func (b *writeBuf) uint64(v uint64) {
|
||||
binary.LittleEndian.PutUint64(*b, v)
|
||||
*b = (*b)[8:]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ package zip
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
"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) {
|
||||
fh := &FileHeader{
|
||||
Name: "foo.txt",
|
||||
|
|
@ -65,17 +93,83 @@ func TestFileHeaderRoundTrip(t *testing.T) {
|
|||
ModifiedTime: 1234,
|
||||
ModifiedDate: 5678,
|
||||
}
|
||||
fi := fh.FileInfo()
|
||||
fh2, err := FileInfoHeader(fi)
|
||||
testHeaderRoundTrip(fh, fh.UncompressedSize, uint64(fh.UncompressedSize), t)
|
||||
}
|
||||
|
||||
// Ignore these fields:
|
||||
fh2.CreatorVersion = 0
|
||||
fh2.ExternalAttrs = 0
|
||||
|
||||
if !reflect.DeepEqual(fh, fh2) {
|
||||
t.Errorf("mismatch\n input=%#v\noutput=%#v\nerr=%v", fh, fh2, err)
|
||||
func TestFileHeaderRoundTrip64(t *testing.T) {
|
||||
fh := &FileHeader{
|
||||
Name: "foo.txt",
|
||||
UncompressedSize64: 9876543210,
|
||||
ModifiedTime: 1234,
|
||||
ModifiedDate: 5678,
|
||||
}
|
||||
if sysfh, ok := fi.Sys().(*FileHeader); !ok && sysfh != fh {
|
||||
t.Errorf("Sys didn't return original *FileHeader")
|
||||
testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -375,6 +375,41 @@ func (b *Reader) ReadString(delim byte) (line string, err error) {
|
|||
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
|
||||
|
||||
// Writer implements buffering for an io.Writer object.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,6 +99,19 @@ func (b *Buffer) grow(n int) int {
|
|||
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
|
||||
// value n is the length of p; err is always nil.
|
||||
// If the buffer becomes too large, Write will panic with
|
||||
|
|
|
|||
|
|
@ -8,20 +8,21 @@ import (
|
|||
. "bytes"
|
||||
"io"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"testing"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const N = 10000 // make this bigger for a larger (and slower) test
|
||||
var data string // test data for write tests
|
||||
var bytes []byte // test data; same as data but as a slice.
|
||||
const N = 10000 // make this bigger for a larger (and slower) test
|
||||
var data string // test data for write tests
|
||||
var testBytes []byte // test data; same as data but as a slice.
|
||||
|
||||
func init() {
|
||||
bytes = make([]byte, N)
|
||||
testBytes = make([]byte, N)
|
||||
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.
|
||||
|
|
@ -84,7 +85,7 @@ func fillBytes(t *testing.T, testname string, buf *Buffer, s string, n int, fub
|
|||
}
|
||||
|
||||
func TestNewBuffer(t *testing.T) {
|
||||
buf := NewBuffer(bytes)
|
||||
buf := NewBuffer(testBytes)
|
||||
check(t, "NewBuffer", buf, data)
|
||||
}
|
||||
|
||||
|
|
@ -187,7 +188,7 @@ func TestLargeByteWrites(t *testing.T) {
|
|||
limit = 9
|
||||
}
|
||||
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))
|
||||
}
|
||||
check(t, "TestLargeByteWrites (3)", &buf, "")
|
||||
|
|
@ -205,7 +206,7 @@ func TestLargeStringReads(t *testing.T) {
|
|||
func TestLargeByteReads(t *testing.T) {
|
||||
var buf Buffer
|
||||
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)))
|
||||
}
|
||||
check(t, "TestLargeByteReads (3)", &buf, "")
|
||||
|
|
@ -219,7 +220,7 @@ func TestMixedReadsAndWrites(t *testing.T) {
|
|||
if i%2 == 0 {
|
||||
s = fillString(t, "TestMixedReadsAndWrites (1)", &buf, s, 1, data[0:wlen])
|
||||
} 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))
|
||||
|
|
@ -240,7 +241,7 @@ func TestNil(t *testing.T) {
|
|||
func TestReadFrom(t *testing.T) {
|
||||
var buf Buffer
|
||||
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
|
||||
b.ReadFrom(&buf)
|
||||
empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data)))
|
||||
|
|
@ -250,7 +251,7 @@ func TestReadFrom(t *testing.T) {
|
|||
func TestWriteTo(t *testing.T) {
|
||||
var buf Buffer
|
||||
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
|
||||
buf.WriteTo(&b)
|
||||
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.
|
||||
func TestReadEmptyAtEOF(t *testing.T) {
|
||||
b := new(Buffer)
|
||||
|
|
|
|||
|
|
@ -333,14 +333,15 @@ func FieldsFunc(s []byte, f func(rune) bool) [][]byte {
|
|||
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.
|
||||
func Join(a [][]byte, sep []byte) []byte {
|
||||
if len(a) == 0 {
|
||||
return []byte{}
|
||||
}
|
||||
if len(a) == 1 {
|
||||
return a[0]
|
||||
// Just return a copy.
|
||||
return append([]byte(nil), a[0]...)
|
||||
}
|
||||
n := len(sep) * (len(a) - 1)
|
||||
for i := 0; i < len(a); i++ {
|
||||
|
|
@ -619,10 +620,8 @@ func Replace(s, old, new []byte, n int) []byte {
|
|||
m = Count(s, old)
|
||||
}
|
||||
if m == 0 {
|
||||
// Nothing to do. Just copy.
|
||||
t := make([]byte, len(s))
|
||||
copy(t, s)
|
||||
return t
|
||||
// Just return a copy.
|
||||
return append([]byte(nil), s...)
|
||||
}
|
||||
if n < 0 || m < n {
|
||||
n = m
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package bytes_test
|
|||
|
||||
import (
|
||||
. "bytes"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"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)
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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' }
|
||||
var fieldsFuncTests = []FieldsTest{
|
||||
{"", []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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,23 +5,24 @@
|
|||
package bytes_test
|
||||
|
||||
import (
|
||||
. "bytes"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
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("world!"))
|
||||
fmt.Fprintf(&b, "world!")
|
||||
b.WriteTo(os.Stdout)
|
||||
// Output: Hello world!
|
||||
}
|
||||
|
||||
func ExampleBuffer_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)
|
||||
io.Copy(os.Stdout, dec)
|
||||
// Output: Gophers rule!
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -334,7 +334,7 @@ var deflateInflateStringTests = []deflateInflateStringTest{
|
|||
{
|
||||
"../testdata/e.txt",
|
||||
"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",
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ type decompressor struct {
|
|||
codebits [numCodes]int
|
||||
|
||||
// Output history, buffer.
|
||||
hist [maxHist]byte
|
||||
hist *[maxHist]byte
|
||||
hp int // current output position in buffer
|
||||
hw int // have written hist[0:hw] already
|
||||
hfull bool // buffer has filled at least once
|
||||
|
|
@ -511,51 +511,49 @@ func (f *decompressor) huffmanBlock() {
|
|||
return
|
||||
}
|
||||
|
||||
p := f.hp - dist
|
||||
if p < 0 {
|
||||
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
|
||||
}
|
||||
if p == len(f.hist) {
|
||||
p = 0
|
||||
}
|
||||
f.copyLen, f.copyDist = length, dist
|
||||
if f.copyHist() {
|
||||
return
|
||||
}
|
||||
}
|
||||
panic("unreached")
|
||||
}
|
||||
|
||||
func (f *decompressor) copyHuff() {
|
||||
length := f.copyLen
|
||||
dist := f.copyDist
|
||||
p := f.hp - dist
|
||||
// copyHist copies f.copyLen bytes from f.hist (f.copyDist bytes ago) to itself.
|
||||
// It reports whether the f.hist buffer is full.
|
||||
func (f *decompressor) copyHist() bool {
|
||||
p := f.hp - f.copyDist
|
||||
if p < 0 {
|
||||
p += len(f.hist)
|
||||
}
|
||||
for i := 0; i < length; i++ {
|
||||
f.hist[f.hp] = f.hist[p]
|
||||
f.hp++
|
||||
p++
|
||||
for f.copyLen > 0 {
|
||||
n := f.copyLen
|
||||
if x := len(f.hist) - f.hp; n > x {
|
||||
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) {
|
||||
f.copyLen = length - (i + 1)
|
||||
// After flush continue copying out of history.
|
||||
f.flush((*decompressor).copyHuff)
|
||||
return
|
||||
return true
|
||||
}
|
||||
if p == len(f.hist) {
|
||||
p = 0
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Continue processing Huffman block.
|
||||
func (f *decompressor) copyHuff() {
|
||||
if f.copyHist() {
|
||||
return
|
||||
}
|
||||
f.huffmanBlock()
|
||||
}
|
||||
|
||||
|
|
@ -590,9 +588,9 @@ func (f *decompressor) dataBlock() {
|
|||
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() {
|
||||
// Read f.dataLen bytes into history,
|
||||
// pausing for reads as history fills.
|
||||
n := f.copyLen
|
||||
for n > 0 {
|
||||
m := len(f.hist) - f.hp
|
||||
|
|
@ -695,6 +693,7 @@ func makeReader(r io.Reader) Reader {
|
|||
func NewReader(r io.Reader) io.ReadCloser {
|
||||
var f decompressor
|
||||
f.r = makeReader(r)
|
||||
f.hist = new([maxHist]byte)
|
||||
f.step = (*decompressor).nextBlock
|
||||
return &f
|
||||
}
|
||||
|
|
@ -706,8 +705,9 @@ func NewReader(r io.Reader) io.ReadCloser {
|
|||
// to read data compressed by NewWriterDict.
|
||||
func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser {
|
||||
var f decompressor
|
||||
f.setDict(dict)
|
||||
f.r = makeReader(r)
|
||||
f.hist = new([maxHist]byte)
|
||||
f.step = (*decompressor).nextBlock
|
||||
f.setDict(dict)
|
||||
return &f
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
@ -114,11 +114,19 @@ func TestReader(t *testing.T) {
|
|||
func benchmarkDecoder(b *testing.B, n int) {
|
||||
b.StopTimer()
|
||||
b.SetBytes(int64(n))
|
||||
buf0, _ := ioutil.ReadFile("../testdata/e.txt")
|
||||
buf0 = buf0[:10000]
|
||||
buf0, err := ioutil.ReadFile("../testdata/e.txt")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if len(buf0) == 0 {
|
||||
b.Fatalf("test file has no data")
|
||||
}
|
||||
compressed := new(bytes.Buffer)
|
||||
w := NewWriter(compressed, LSB, 8)
|
||||
for i := 0; i < n; i += len(buf0) {
|
||||
if len(buf0) > n-i {
|
||||
buf0 = buf0[:n-i]
|
||||
}
|
||||
io.Copy(w, bytes.NewBuffer(buf0))
|
||||
}
|
||||
w.Close()
|
||||
|
|
|
|||
|
|
@ -131,13 +131,14 @@ func (e *encoder) incHi() error {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return 0, e.err
|
||||
}
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n = len(p)
|
||||
litMask := uint32(1<<e.litWidth - 1)
|
||||
code := e.savedCode
|
||||
if code == invalidCode {
|
||||
|
|
@ -167,11 +168,11 @@ loop:
|
|||
code = literal
|
||||
// Increment e.hi, the next implied code. If we run out of codes, reset
|
||||
// the encoder state (including clearing the hash table) and continue.
|
||||
if err := e.incHi(); err != nil {
|
||||
if err == errOutOfCodes {
|
||||
if err1 := e.incHi(); err1 != nil {
|
||||
if err1 == errOutOfCodes {
|
||||
continue
|
||||
}
|
||||
e.err = err
|
||||
e.err = err1
|
||||
return 0, e.err
|
||||
}
|
||||
// Otherwise, insert key -> e.hi into the map that e.table represents.
|
||||
|
|
@ -184,7 +185,7 @@ loop:
|
|||
}
|
||||
}
|
||||
e.savedCode = code
|
||||
return len(p), nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Close closes the encoder, flushing any pending output. It does not close or
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
b.StopTimer()
|
||||
b.SetBytes(int64(n))
|
||||
buf0, _ := ioutil.ReadFile("../testdata/e.txt")
|
||||
buf0 = buf0[:10000]
|
||||
buf0, err := ioutil.ReadFile("../testdata/e.txt")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if len(buf0) == 0 {
|
||||
b.Fatalf("test file has no data")
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
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
|
|
@ -11,7 +11,7 @@ and compress during writing. For example, to write compressed data
|
|||
to a buffer:
|
||||
|
||||
var b bytes.Buffer
|
||||
w, err := zlib.NewWriter(&b)
|
||||
w := zlib.NewWriter(&b)
|
||||
w.Write([]byte("hello, world\n"))
|
||||
w.Close()
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ func Remove(h Interface, i int) interface{} {
|
|||
func up(h Interface, j int) {
|
||||
for {
|
||||
i := (j - 1) / 2 // parent
|
||||
if i == j || h.Less(i, j) {
|
||||
if i == j || !h.Less(j, i) {
|
||||
break
|
||||
}
|
||||
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) {
|
||||
j = j2 // = 2*i + 2 // right child
|
||||
}
|
||||
if h.Less(i, j) {
|
||||
if !h.Less(j, i) {
|
||||
break
|
||||
}
|
||||
h.Swap(i, j)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,201 +11,185 @@
|
|||
//
|
||||
package list
|
||||
|
||||
// Element is an element in the linked list.
|
||||
// Element is an element of a linked list.
|
||||
type Element struct {
|
||||
// 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
|
||||
|
||||
// The list to which this element belongs.
|
||||
list *List
|
||||
|
||||
// The contents of this list element.
|
||||
// The value stored with this element.
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// 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.
|
||||
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.
|
||||
// The zero value for List is an empty list ready to use.
|
||||
type List struct {
|
||||
front, back *Element
|
||||
len int
|
||||
root Element // sentinel list element, only &root, root.prev, and root.next are used
|
||||
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 {
|
||||
l.front = nil
|
||||
l.back = nil
|
||||
l.root.next = &l.root
|
||||
l.root.prev = &l.root
|
||||
l.len = 0
|
||||
return l
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (l *List) Front() *Element { return l.front }
|
||||
// Len returns the number of elements of list l.
|
||||
func (l *List) Len() int { return l.len }
|
||||
|
||||
// Back returns the last element in the list.
|
||||
func (l *List) Back() *Element { return l.back }
|
||||
// Front returns the first element of list l or nil
|
||||
func (l *List) Front() *Element {
|
||||
if l.len == 0 {
|
||||
return nil
|
||||
}
|
||||
return l.root.next
|
||||
}
|
||||
|
||||
// Remove removes the element from the list
|
||||
// and returns its Value.
|
||||
// Back returns the last element of list l or nil.
|
||||
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{} {
|
||||
l.remove(e)
|
||||
e.list = nil // do what remove does not
|
||||
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)
|
||||
}
|
||||
return e.Value
|
||||
}
|
||||
|
||||
// remove the element from the list, but do not clear the Element's list field.
|
||||
// This is so that other List methods may use remove when relocating Elements
|
||||
// without needing to restore the list field.
|
||||
func (l *List) remove(e *Element) {
|
||||
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--
|
||||
// Pushfront inserts a new element e with value v at the front of list l and returns e.
|
||||
func (l *List) PushFront(v interface{}) *Element {
|
||||
l.lazyInit()
|
||||
return l.insertValue(v, &l.root)
|
||||
}
|
||||
|
||||
func (l *List) insertBefore(e *Element, mark *Element) {
|
||||
if mark.prev == nil {
|
||||
// new front of the list
|
||||
l.front = e
|
||||
} else {
|
||||
mark.prev.next = e
|
||||
}
|
||||
e.prev = mark.prev
|
||||
mark.prev = e
|
||||
e.next = mark
|
||||
l.len++
|
||||
// PushBack inserts a new element e with value v at the back of list l and returns e.
|
||||
func (l *List) PushBack(v interface{}) *Element {
|
||||
l.lazyInit()
|
||||
return l.insertValue(v, l.root.prev)
|
||||
}
|
||||
|
||||
func (l *List) insertAfter(e *Element, mark *Element) {
|
||||
if mark.next == nil {
|
||||
// new back of the list
|
||||
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 {
|
||||
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
|
||||
// If mark is not an element of l, the list is not modified.
|
||||
func (l *List) InsertBefore(v interface{}, mark *Element) *Element {
|
||||
if mark.list != l {
|
||||
return nil
|
||||
}
|
||||
e := &Element{nil, nil, l, value}
|
||||
l.insertBefore(e, mark)
|
||||
return e
|
||||
// see comment in List.Remove about initialization of l
|
||||
return l.insertValue(v, mark.prev)
|
||||
}
|
||||
|
||||
// InsertAfter inserts the value immediately after mark and returns a new Element containing the value.
|
||||
func (l *List) InsertAfter(value interface{}, mark *Element) *Element {
|
||||
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
|
||||
// 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 {
|
||||
return nil
|
||||
}
|
||||
e := &Element{nil, nil, l, value}
|
||||
l.insertAfter(e, mark)
|
||||
return e
|
||||
// see comment in List.Remove about initialization of l
|
||||
return l.insertValue(v, mark)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if e.list != l || l.front == e {
|
||||
if e.list != l || l.root.next == e {
|
||||
return
|
||||
}
|
||||
l.remove(e)
|
||||
l.insertFront(e)
|
||||
// see comment in List.Remove about initialization of l
|
||||
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) {
|
||||
if e.list != l || l.back == e {
|
||||
if e.list != l || l.root.prev == e {
|
||||
return
|
||||
}
|
||||
l.remove(e)
|
||||
l.insertBack(e)
|
||||
// see comment in List.Remove about initialization of l
|
||||
l.insert(l.remove(e), l.root.prev)
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
func (l *List) Len() int { return l.len }
|
||||
|
||||
// PushBackList inserts each element of ol at the back of the list.
|
||||
func (l *List) PushBackList(ol *List) {
|
||||
last := ol.Back()
|
||||
for e := ol.Front(); e != nil; e = e.Next() {
|
||||
l.PushBack(e.Value)
|
||||
if e == last {
|
||||
break
|
||||
}
|
||||
// PuchBackList inserts a copy of an other list at the back of list l.
|
||||
// The lists l and other may be the same.
|
||||
func (l *List) PushBackList(other *List) {
|
||||
l.lazyInit()
|
||||
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
|
||||
l.insertValue(e.Value, l.root.prev)
|
||||
}
|
||||
}
|
||||
|
||||
// PushFrontList inserts each element of ol at the front of the list. The ordering of the passed list is preserved.
|
||||
func (l *List) PushFrontList(ol *List) {
|
||||
first := ol.Front()
|
||||
for e := ol.Back(); e != nil; e = e.Prev() {
|
||||
l.PushFront(e.Value)
|
||||
if e == first {
|
||||
break
|
||||
}
|
||||
// PushFrontList inserts a copy of an other list at the front of list l.
|
||||
// The lists l and other may be the same.
|
||||
func (l *List) PushFrontList(other *List) {
|
||||
l.lazyInit()
|
||||
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
|
||||
l.insertValue(e.Value, &l.root)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,65 +4,75 @@
|
|||
|
||||
package list
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
import "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) {
|
||||
if len(es) == 0 {
|
||||
if l.front != nil || l.back != nil {
|
||||
t.Errorf("l.front/l.back = %v/%v should be nil/nil", l.front, l.back)
|
||||
}
|
||||
root := &l.root
|
||||
|
||||
if !checkListLen(t, l, len(es)) {
|
||||
return
|
||||
}
|
||||
|
||||
if l.front != es[0] {
|
||||
t.Errorf("l.front = %v, want %v", l.front, es[0])
|
||||
}
|
||||
if last := es[len(es)-1]; l.back != last {
|
||||
t.Errorf("l.back = %v, want %v", l.back, last)
|
||||
// zero length lists must be the zero value or properly initialized (sentinel circle)
|
||||
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)
|
||||
}
|
||||
return
|
||||
}
|
||||
// len(es) > 0
|
||||
|
||||
// check internal and external prev/next connections
|
||||
for i, e := range es {
|
||||
var e_prev, e_next *Element = nil, nil
|
||||
prev := root
|
||||
Prev := (*Element)(nil)
|
||||
if i > 0 {
|
||||
e_prev = es[i-1]
|
||||
prev = es[i-1]
|
||||
Prev = prev
|
||||
}
|
||||
if i < len(es)-1 {
|
||||
e_next = es[i+1]
|
||||
if p := e.prev; p != prev {
|
||||
t.Errorf("elt[%d](%p).prev = %p, want %p", i, e, p, prev)
|
||||
}
|
||||
if e.prev != e_prev {
|
||||
t.Errorf("elt #%d (%v) has prev=%v, want %v", i, e, e.prev, e_prev)
|
||||
if p := e.Prev(); p != 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) {
|
||||
if an := l.Len(); an != n {
|
||||
t.Errorf("l.Len() = %d, want %d", an, n)
|
||||
next := root
|
||||
Next := (*Element)(nil)
|
||||
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) {
|
||||
l := New()
|
||||
checkListPointers(t, l, []*Element{})
|
||||
checkListLen(t, l, 0)
|
||||
|
||||
// Single element list
|
||||
e := l.PushFront("a")
|
||||
checkListLen(t, l, 1)
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.MoveToFront(e)
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
l.MoveToBack(e)
|
||||
checkListPointers(t, l, []*Element{e})
|
||||
checkListLen(t, l, 1)
|
||||
l.Remove(e)
|
||||
checkListPointers(t, l, []*Element{})
|
||||
checkListLen(t, l, 0)
|
||||
|
||||
// Bigger list
|
||||
e2 := l.PushFront(2)
|
||||
|
|
@ -70,11 +80,9 @@ func TestList(t *testing.T) {
|
|||
e3 := l.PushBack(3)
|
||||
e4 := l.PushBack("banana")
|
||||
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
|
||||
checkListLen(t, l, 4)
|
||||
|
||||
l.Remove(e2)
|
||||
checkListPointers(t, l, []*Element{e1, e3, e4})
|
||||
checkListLen(t, l, 3)
|
||||
|
||||
l.MoveToFront(e3) // move from middle
|
||||
checkListPointers(t, l, []*Element{e3, e1, e4})
|
||||
|
|
@ -121,7 +129,7 @@ func TestList(t *testing.T) {
|
|||
}
|
||||
}
|
||||
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
|
||||
|
|
@ -131,19 +139,18 @@ func TestList(t *testing.T) {
|
|||
l.Remove(e)
|
||||
}
|
||||
checkListPointers(t, l, []*Element{})
|
||||
checkListLen(t, l, 0)
|
||||
}
|
||||
|
||||
func checkList(t *testing.T, l *List, es []interface{}) {
|
||||
if l.Len() != len(es) {
|
||||
t.Errorf("list has len=%v, want %v", l.Len(), len(es))
|
||||
if !checkListLen(t, l, len(es)) {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
le := e.Value.(int)
|
||||
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++
|
||||
}
|
||||
|
|
@ -202,8 +209,27 @@ func TestRemove(t *testing.T) {
|
|||
e := l.Front()
|
||||
l.Remove(e)
|
||||
checkListPointers(t, l, []*Element{e2})
|
||||
checkListLen(t, l, 1)
|
||||
l.Remove(e)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,7 +221,10 @@ L:
|
|||
if tt.dec != nil {
|
||||
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 {
|
||||
if 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) {
|
||||
b.StopTimer()
|
||||
tt := encryptTests[0]
|
||||
c, err := NewCipher(tt.key)
|
||||
if err != nil {
|
||||
b.Fatal("NewCipher:", err)
|
||||
}
|
||||
out := make([]byte, len(tt.in))
|
||||
b.StartTimer()
|
||||
b.SetBytes(int64(len(out)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c.Encrypt(out, tt.in)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecrypt(b *testing.B) {
|
||||
tt := 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
package aes
|
||||
|
||||
// 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
|
||||
|
||||
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.
|
||||
func decryptBlock(xk []uint32, dst, src []byte) {
|
||||
func decryptBlockGo(xk []uint32, dst, src []byte) {
|
||||
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])
|
||||
|
|
@ -139,7 +139,7 @@ func rotw(w uint32) uint32 { return w<<8 | w>>24 }
|
|||
|
||||
// Key expansion algorithm. See FIPS-197, Figure 11.
|
||||
// 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.
|
||||
var i int
|
||||
nk := len(key) / 4
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ func NewCipher(key []byte) (cipher.Block, error) {
|
|||
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -140,14 +140,16 @@ func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
|
|||
w := new(big.Int).ModInverse(s, N)
|
||||
|
||||
u1 := e.Mul(e, w)
|
||||
u1.Mod(u1, N)
|
||||
u2 := w.Mul(r, w)
|
||||
u2.Mod(u2, N)
|
||||
|
||||
x1, y1 := c.ScalarBaseMult(u1.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
|
||||
}
|
||||
x, _ := c.Add(x1, y1, x2, y2)
|
||||
x.Mod(x, N)
|
||||
return x.Cmp(r) == 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,19 @@
|
|||
package ecdsa
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"compress/bzip2"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -72,156 +80,112 @@ func fromHex(s string) *big.Int {
|
|||
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) {
|
||||
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),
|
||||
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
|
||||
}
|
||||
t.Fatalf("error reading from input: %s", err)
|
||||
}
|
||||
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)
|
||||
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)
|
||||
}
|
||||
if testing.Short() {
|
||||
break
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ type Curve interface {
|
|||
// Double returns 2*(x,y)
|
||||
Double(x1, y1 *big.Int) (x, y *big.Int)
|
||||
// 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)
|
||||
// ScalarBaseMult returns k*G, where G is the base point of the group and k
|
||||
// is an integer in big-endian form.
|
||||
ScalarBaseMult(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 is an integer in big-endian form.
|
||||
ScalarBaseMult(k []byte) (x, y *big.Int)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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) {
|
||||
if z.Sign() == 0 {
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
|
||||
zinv := new(big.Int).ModInverse(z, curve.P)
|
||||
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) {
|
||||
z := new(big.Int).SetInt64(1)
|
||||
return curve.affineFromJacobian(curve.addJacobian(x1, y1, z, x2, y2, z))
|
||||
z1 := zForAffine(x1, y1)
|
||||
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
|
||||
// (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) {
|
||||
// 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.Mod(z1z1, curve.P)
|
||||
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.Mod(u2, curve.P)
|
||||
h := new(big.Int).Sub(u2, u1)
|
||||
xEqual := h.Sign() == 0
|
||||
if h.Sign() == -1 {
|
||||
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 {
|
||||
r.Add(r, curve.P)
|
||||
}
|
||||
yEqual := r.Sign() == 0
|
||||
if xEqual && yEqual {
|
||||
return curve.doubleJacobian(x1, y1, z1)
|
||||
}
|
||||
r.Lsh(r, 1)
|
||||
v := new(big.Int).Mul(u1, i)
|
||||
|
||||
x3 := new(big.Int).Set(r)
|
||||
x3.Set(r)
|
||||
x3.Mul(x3, x3)
|
||||
x3.Sub(x3, j)
|
||||
x3.Sub(x3, v)
|
||||
x3.Sub(x3, v)
|
||||
x3.Mod(x3, curve.P)
|
||||
|
||||
y3 := new(big.Int).Set(r)
|
||||
y3.Set(r)
|
||||
v.Sub(v, x3)
|
||||
y3.Mul(y3, v)
|
||||
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.Mod(y3, curve.P)
|
||||
|
||||
z3 := new(big.Int).Add(z1, z2)
|
||||
z3.Add(z1, z2)
|
||||
z3.Mul(z3, z3)
|
||||
z3.Sub(z3, z1z1)
|
||||
if z3.Sign() == -1 {
|
||||
z3.Add(z3, curve.P)
|
||||
}
|
||||
z3.Sub(z3, z2z2)
|
||||
if z3.Sign() == -1 {
|
||||
z3.Add(z3, curve.P)
|
||||
}
|
||||
z3.Mul(z3, h)
|
||||
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) {
|
||||
z1 := new(big.Int).SetInt64(1)
|
||||
z1 := zForAffine(x1, y1)
|
||||
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) {
|
||||
// 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)
|
||||
x := Bx
|
||||
y := By
|
||||
z := Bz
|
||||
x, y, z := new(big.Int), new(big.Int), new(big.Int)
|
||||
|
||||
seenFirstTrue := false
|
||||
for _, byte := range k {
|
||||
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 !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
|
||||
}
|
||||
}
|
||||
|
||||
if !seenFirstTrue {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return curve.affineFromJacobian(x, y, z)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
b.ResetTimer()
|
||||
p224 := P224()
|
||||
|
|
|
|||
|
|
@ -80,10 +80,14 @@ func (p224Curve) Add(bigX1, bigY1, bigX2, bigY2 *big.Int) (x, y *big.Int) {
|
|||
|
||||
p224FromBig(&x1, bigX1)
|
||||
p224FromBig(&y1, bigY1)
|
||||
z1[0] = 1
|
||||
if bigX1.Sign() != 0 || bigY1.Sign() != 0 {
|
||||
z1[0] = 1
|
||||
}
|
||||
p224FromBig(&x2, bigX2)
|
||||
p224FromBig(&y2, bigY2)
|
||||
z2[0] = 1
|
||||
if bigX2.Sign() != 0 || bigY2.Sign() != 0 {
|
||||
z2[0] = 1
|
||||
}
|
||||
|
||||
p224AddJacobian(&x3, &y3, &z3, &x1, &y1, &z1, &x2, &y2, &z2)
|
||||
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.
|
||||
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
|
||||
//
|
||||
// a[i] + b[i] < 2**32
|
||||
|
|
@ -406,7 +448,7 @@ func p224Contract(out, in *p224FieldElement) {
|
|||
// true.
|
||||
top4AllOnes := uint32(0xffffffff)
|
||||
for i := 4; i < 8; i++ {
|
||||
top4AllOnes &= (out[i] & bottom28Bits) - 1
|
||||
top4AllOnes &= out[i]
|
||||
}
|
||||
top4AllOnes |= 0xf0000000
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
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 c p224LargeFieldElement
|
||||
|
||||
z1IsZero := p224IsZero(z1)
|
||||
z2IsZero := p224IsZero(z2)
|
||||
|
||||
// Z1Z1 = Z1²
|
||||
p224Square(&z1z1, z1, &c)
|
||||
// Z2Z2 = Z2²
|
||||
|
|
@ -480,6 +525,7 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
|
|||
// H = U2-U1
|
||||
p224Sub(&h, &u2, &u1)
|
||||
p224Reduce(&h)
|
||||
xEqual := p224IsZero(&h)
|
||||
// I = (2*H)²
|
||||
for j := 0; j < 8; j++ {
|
||||
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)
|
||||
p224Sub(&r, &s2, &s1)
|
||||
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++ {
|
||||
r[i] <<= 1
|
||||
}
|
||||
|
|
@ -524,6 +575,13 @@ func p224AddJacobian(x3, y3, z3, x1, y1, z1, x2, y2, z2 *p224FieldElement) {
|
|||
p224Mul(&z1z1, &z1z1, &r, &c)
|
||||
p224Sub(y3, &z1z1, &s1)
|
||||
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.
|
||||
|
|
@ -593,22 +651,19 @@ func p224CopyConditional(out, in *p224FieldElement, control uint32) {
|
|||
func p224ScalarMult(outX, outY, outZ, inX, inY, inZ *p224FieldElement, scalar []byte) {
|
||||
var xx, yy, zz p224FieldElement
|
||||
for i := 0; i < 8; i++ {
|
||||
outX[i] = 0
|
||||
outY[i] = 0
|
||||
outZ[i] = 0
|
||||
}
|
||||
|
||||
firstBit := uint32(1)
|
||||
for _, byte := range scalar {
|
||||
for bitNum := uint(0); bitNum < 8; bitNum++ {
|
||||
p224DoubleJacobian(outX, outY, outZ, outX, outY, outZ)
|
||||
bit := uint32((byte >> (7 - bitNum)) & 1)
|
||||
p224AddJacobian(&xx, &yy, &zz, inX, inY, inZ, outX, outY, outZ)
|
||||
p224CopyConditional(outX, inX, firstBit&bit)
|
||||
p224CopyConditional(outY, inY, firstBit&bit)
|
||||
p224CopyConditional(outZ, inZ, firstBit&bit)
|
||||
p224CopyConditional(outX, &xx, ^firstBit&bit)
|
||||
p224CopyConditional(outY, &yy, ^firstBit&bit)
|
||||
p224CopyConditional(outZ, &zz, ^firstBit&bit)
|
||||
firstBit = firstBit & ^bit
|
||||
p224CopyConditional(outX, &xx, bit)
|
||||
p224CopyConditional(outY, &yy, bit)
|
||||
p224CopyConditional(outZ, &zz, bit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -618,16 +673,8 @@ func p224ToAffine(x, y, z *p224FieldElement) (*big.Int, *big.Int) {
|
|||
var zinv, zinvsq, outx, outy p224FieldElement
|
||||
var tmp p224LargeFieldElement
|
||||
|
||||
isPointAtInfinity := true
|
||||
for i := 0; i < 8; i++ {
|
||||
if z[i] != 0 {
|
||||
isPointAtInfinity = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if isPointAtInfinity {
|
||||
return nil, nil
|
||||
if isPointAtInfinity := p224IsZero(z); isPointAtInfinity == 1 {
|
||||
return new(big.Int), new(big.Int)
|
||||
}
|
||||
|
||||
p224Invert(&zinv, z)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
`
|
||||
|
|
@ -21,26 +21,26 @@ const Size = 16
|
|||
const BlockSize = 64
|
||||
|
||||
const (
|
||||
_Chunk = 64
|
||||
_Init0 = 0x67452301
|
||||
_Init1 = 0xEFCDAB89
|
||||
_Init2 = 0x98BADCFE
|
||||
_Init3 = 0x10325476
|
||||
chunk = 64
|
||||
init0 = 0x67452301
|
||||
init1 = 0xEFCDAB89
|
||||
init2 = 0x98BADCFE
|
||||
init3 = 0x10325476
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
s [4]uint32
|
||||
x [_Chunk]byte
|
||||
x [chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
}
|
||||
|
||||
func (d *digest) Reset() {
|
||||
d.s[0] = _Init0
|
||||
d.s[1] = _Init1
|
||||
d.s[2] = _Init2
|
||||
d.s[3] = _Init3
|
||||
d.s[0] = init0
|
||||
d.s[1] = init1
|
||||
d.s[2] = init2
|
||||
d.s[3] = init3
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
}
|
||||
|
|
@ -61,21 +61,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
|
|||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > _Chunk-d.nx {
|
||||
n = _Chunk - d.nx
|
||||
if n > chunk-d.nx {
|
||||
n = chunk - d.nx
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
d.x[d.nx+i] = p[i]
|
||||
}
|
||||
d.nx += n
|
||||
if d.nx == _Chunk {
|
||||
_Block(d, d.x[0:])
|
||||
if d.nx == chunk {
|
||||
block(d, d.x[0:chunk])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := _Block(d, p)
|
||||
p = p[n:]
|
||||
if len(p) >= chunk {
|
||||
n := len(p) &^ (chunk - 1)
|
||||
block(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,3 +78,28 @@ func ExampleNew() {
|
|||
fmt.Printf("%x", h.Sum(nil))
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
// table[i] = int((1<<32) * abs(sin(i+1 radians))).
|
||||
var table = []uint32{
|
||||
// round 1
|
||||
0xd76aa478,
|
||||
0xe8c7b756,
|
||||
0x242070db,
|
||||
0xc1bdceee,
|
||||
0xf57c0faf,
|
||||
0x4787c62a,
|
||||
0xa8304613,
|
||||
0xfd469501,
|
||||
0x698098d8,
|
||||
0x8b44f7af,
|
||||
0xffff5bb1,
|
||||
0x895cd7be,
|
||||
0x6b901122,
|
||||
0xfd987193,
|
||||
0xa679438e,
|
||||
0x49b40821,
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// round 2
|
||||
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 {
|
||||
func block(dig *digest, p []byte) {
|
||||
a := dig.s[0]
|
||||
b := dig.s[1]
|
||||
c := dig.s[2]
|
||||
d := dig.s[3]
|
||||
n := 0
|
||||
var X [16]uint32
|
||||
for len(p) >= _Chunk {
|
||||
var X *[16]uint32
|
||||
var xbuf [16]uint32
|
||||
for len(p) >= chunk {
|
||||
aa, bb, cc, dd := a, b, c, d
|
||||
|
||||
j := 0
|
||||
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
|
||||
j += 4
|
||||
// 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 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.
|
||||
for i := uint(0); i < 16; i++ {
|
||||
x := i
|
||||
s := shift1[i%4]
|
||||
f := ((c ^ d) & b) ^ d
|
||||
a += f + X[x] + table[i]
|
||||
a = a<<s | a>>(32-s) + b
|
||||
a, b, c, d = d, a, b, c
|
||||
}
|
||||
|
||||
a += (((c ^ d) & b) ^ d) + X[0] + 3614090360
|
||||
a = a<<7 | a>>(32-7) + b
|
||||
|
||||
d += (((b ^ c) & a) ^ c) + X[1] + 3905402710
|
||||
d = d<<12 | d>>(32-12) + a
|
||||
|
||||
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.
|
||||
for i := uint(0); i < 16; i++ {
|
||||
x := (1 + 5*i) % 16
|
||||
s := shift2[i%4]
|
||||
g := ((b ^ c) & d) ^ c
|
||||
a += g + X[x] + table[i+16]
|
||||
a = a<<s | a>>(32-s) + b
|
||||
a, b, c, d = d, a, b, c
|
||||
}
|
||||
|
||||
a += (((b ^ c) & d) ^ c) + X[(1+5*0)&15] + 4129170786
|
||||
a = a<<5 | a>>(32-5) + b
|
||||
|
||||
d += (((a ^ b) & c) ^ b) + X[(1+5*1)&15] + 3225465664
|
||||
d = d<<9 | d>>(32-9) + a
|
||||
|
||||
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.
|
||||
for i := uint(0); i < 16; i++ {
|
||||
x := (5 + 3*i) % 16
|
||||
s := shift3[i%4]
|
||||
h := b ^ c ^ d
|
||||
a += h + X[x] + table[i+32]
|
||||
a = a<<s | a>>(32-s) + b
|
||||
a, b, c, d = d, a, b, c
|
||||
}
|
||||
|
||||
a += (b ^ c ^ d) + X[(5+3*0)&15] + 4294588738
|
||||
a = a<<4 | a>>(32-4) + b
|
||||
|
||||
d += (a ^ b ^ c) + X[(5+3*1)&15] + 2272392833
|
||||
d = d<<11 | d>>(32-11) + a
|
||||
|
||||
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.
|
||||
for i := uint(0); i < 16; i++ {
|
||||
x := (7 * i) % 16
|
||||
s := shift4[i%4]
|
||||
j := c ^ (b | ^d)
|
||||
a += j + X[x] + table[i+48]
|
||||
a = a<<s | a>>(32-s) + b
|
||||
a, b, c, d = d, a, b, c
|
||||
}
|
||||
|
||||
a += (c ^ (b | ^d)) + X[(7*0)&15] + 4096336452
|
||||
a = a<<6 | a>>(32-6) + b
|
||||
|
||||
d += (b ^ (a | ^c)) + X[(7*1)&15] + 1126891415
|
||||
d = d<<10 | d>>(32-10) + a
|
||||
|
||||
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
|
||||
b += bb
|
||||
c += cc
|
||||
d += dd
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
p = p[chunk:]
|
||||
}
|
||||
|
||||
dig.s[0] = a
|
||||
dig.s[1] = b
|
||||
dig.s[2] = c
|
||||
dig.s[3] = d
|
||||
return n
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// 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
|
||||
// generator.
|
||||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"crypto/cipher"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -22,7 +23,13 @@ import (
|
|||
// Easy implementation: read from /dev/urandom.
|
||||
// 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.
|
||||
type devReader struct {
|
||||
|
|
@ -39,14 +46,17 @@ func (r *devReader) Read(b []byte) (n int, err error) {
|
|||
if f == nil {
|
||||
return 0, err
|
||||
}
|
||||
r.f = bufio.NewReader(f)
|
||||
if runtime.GOOS == "plan9" {
|
||||
r.f = f
|
||||
} else {
|
||||
r.f = bufio.NewReader(f)
|
||||
}
|
||||
}
|
||||
return r.f.Read(b)
|
||||
}
|
||||
|
||||
// Alternate pseudo-random implementation for use on
|
||||
// systems without a reliable /dev/urandom. So far we
|
||||
// haven't needed it.
|
||||
// systems without a reliable /dev/urandom.
|
||||
|
||||
// newReader returns a new pseudorandom generator that
|
||||
// seeds itself by reading from entropy. If entropy == nil,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ import (
|
|||
// WARNING: use of this function to encrypt plaintexts other than session keys
|
||||
// is dangerous. Use RSA OAEP in new protocols.
|
||||
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
|
||||
if len(msg) > k-11 {
|
||||
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.
|
||||
// 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) {
|
||||
if err := checkPub(&priv.PublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valid, out, err := decryptPKCS1v15(rand, priv, ciphertext)
|
||||
if err == nil && valid == 0 {
|
||||
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
|
||||
// (Crypto '98).
|
||||
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
|
||||
if k-(len(key)+3+8) < 0 {
|
||||
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) {
|
||||
hashLen = hash.Size()
|
||||
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]
|
||||
if !ok {
|
||||
return 0, nil, errors.New("unsupported hash function")
|
||||
return 0, nil, errors.New("crypto/rsa: unsupported hash function")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,30 @@ type PublicKey struct {
|
|||
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
|
||||
type PrivateKey struct {
|
||||
PublicKey // public part.
|
||||
|
|
@ -57,13 +81,17 @@ type CRTValue struct {
|
|||
// Validate performs basic sanity checks on the key.
|
||||
// It returns nil if the key is valid, or else an error describing a problem.
|
||||
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
|
||||
// just a sanity check. Since the random witnesses chosen by
|
||||
// ProbablyPrime are deterministic, given the candidate number, it's
|
||||
// easy for an attack to generate composites that pass this test.
|
||||
for _, prime := range priv.Primes {
|
||||
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)
|
||||
}
|
||||
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 {
|
||||
pminus1 := new(big.Int).Sub(prime, bigOne)
|
||||
totient.Mul(totient, pminus1)
|
||||
}
|
||||
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")
|
||||
congruence.Mod(de, pminus1)
|
||||
if congruence.Cmp(bigOne) != 0 {
|
||||
return errors.New("crypto/rsa: invalid exponents")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -118,7 +142,7 @@ func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (priv *Priva
|
|||
priv.E = 65537
|
||||
|
||||
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)
|
||||
|
|
@ -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
|
||||
// twice the hash length plus 2.
|
||||
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()
|
||||
k := (pub.N.BitLen() + 7) / 8
|
||||
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.
|
||||
// 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) {
|
||||
if err := checkPub(&priv.PublicKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k := (priv.N.BitLen() + 7) / 8
|
||||
if len(ciphertext) > k ||
|
||||
k < hash.Size()*2+2 {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,24 @@ func Test4PrimeKeyGeneration(t *testing.T) {
|
|||
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) {
|
||||
if err := priv.Validate(); err != nil {
|
||||
t.Errorf("Validate() failed: %s", err)
|
||||
|
|
|
|||
|
|
@ -21,28 +21,28 @@ const Size = 20
|
|||
const BlockSize = 64
|
||||
|
||||
const (
|
||||
_Chunk = 64
|
||||
_Init0 = 0x67452301
|
||||
_Init1 = 0xEFCDAB89
|
||||
_Init2 = 0x98BADCFE
|
||||
_Init3 = 0x10325476
|
||||
_Init4 = 0xC3D2E1F0
|
||||
chunk = 64
|
||||
init0 = 0x67452301
|
||||
init1 = 0xEFCDAB89
|
||||
init2 = 0x98BADCFE
|
||||
init3 = 0x10325476
|
||||
init4 = 0xC3D2E1F0
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
h [5]uint32
|
||||
x [_Chunk]byte
|
||||
x [chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
}
|
||||
|
||||
func (d *digest) Reset() {
|
||||
d.h[0] = _Init0
|
||||
d.h[1] = _Init1
|
||||
d.h[2] = _Init2
|
||||
d.h[3] = _Init3
|
||||
d.h[4] = _Init4
|
||||
d.h[0] = init0
|
||||
d.h[1] = init1
|
||||
d.h[2] = init2
|
||||
d.h[3] = init3
|
||||
d.h[4] = init4
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
}
|
||||
|
|
@ -63,21 +63,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
|
|||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > _Chunk-d.nx {
|
||||
n = _Chunk - d.nx
|
||||
if n > chunk-d.nx {
|
||||
n = chunk - d.nx
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
d.x[d.nx+i] = p[i]
|
||||
}
|
||||
d.nx += n
|
||||
if d.nx == _Chunk {
|
||||
_Block(d, d.x[0:])
|
||||
if d.nx == chunk {
|
||||
block(d, d.x[0:])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := _Block(d, p)
|
||||
p = p[n:]
|
||||
if len(p) >= chunk {
|
||||
n := len(p) &^ (chunk - 1)
|
||||
block(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,3 +79,28 @@ func ExampleNew() {
|
|||
fmt.Printf("% x", h.Sum(nil))
|
||||
// Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd
|
||||
}
|
||||
|
||||
var bench = 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,11 @@ const (
|
|||
_K3 = 0xCA62C1D6
|
||||
)
|
||||
|
||||
func _Block(dig *digest, p []byte) int {
|
||||
func block(dig *digest, p []byte) {
|
||||
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]
|
||||
for len(p) >= _Chunk {
|
||||
for len(p) >= chunk {
|
||||
// Can interlace the computation of w with the
|
||||
// rounds below if needed for speed.
|
||||
for i := 0; i < 16; i++ {
|
||||
|
|
@ -72,10 +71,8 @@ func _Block(dig *digest, p []byte) int {
|
|||
h3 += d
|
||||
h4 += e
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
p = p[chunk:]
|
||||
}
|
||||
|
||||
dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] = h0, h1, h2, h3, h4
|
||||
return n
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,29 +26,29 @@ const Size224 = 28
|
|||
const BlockSize = 64
|
||||
|
||||
const (
|
||||
_Chunk = 64
|
||||
_Init0 = 0x6A09E667
|
||||
_Init1 = 0xBB67AE85
|
||||
_Init2 = 0x3C6EF372
|
||||
_Init3 = 0xA54FF53A
|
||||
_Init4 = 0x510E527F
|
||||
_Init5 = 0x9B05688C
|
||||
_Init6 = 0x1F83D9AB
|
||||
_Init7 = 0x5BE0CD19
|
||||
_Init0_224 = 0xC1059ED8
|
||||
_Init1_224 = 0x367CD507
|
||||
_Init2_224 = 0x3070DD17
|
||||
_Init3_224 = 0xF70E5939
|
||||
_Init4_224 = 0xFFC00B31
|
||||
_Init5_224 = 0x68581511
|
||||
_Init6_224 = 0x64F98FA7
|
||||
_Init7_224 = 0xBEFA4FA4
|
||||
chunk = 64
|
||||
init0 = 0x6A09E667
|
||||
init1 = 0xBB67AE85
|
||||
init2 = 0x3C6EF372
|
||||
init3 = 0xA54FF53A
|
||||
init4 = 0x510E527F
|
||||
init5 = 0x9B05688C
|
||||
init6 = 0x1F83D9AB
|
||||
init7 = 0x5BE0CD19
|
||||
init0_224 = 0xC1059ED8
|
||||
init1_224 = 0x367CD507
|
||||
init2_224 = 0x3070DD17
|
||||
init3_224 = 0xF70E5939
|
||||
init4_224 = 0xFFC00B31
|
||||
init5_224 = 0x68581511
|
||||
init6_224 = 0x64F98FA7
|
||||
init7_224 = 0xBEFA4FA4
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
h [8]uint32
|
||||
x [_Chunk]byte
|
||||
x [chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
is224 bool // mark if this digest is SHA-224
|
||||
|
|
@ -56,23 +56,23 @@ type digest struct {
|
|||
|
||||
func (d *digest) Reset() {
|
||||
if !d.is224 {
|
||||
d.h[0] = _Init0
|
||||
d.h[1] = _Init1
|
||||
d.h[2] = _Init2
|
||||
d.h[3] = _Init3
|
||||
d.h[4] = _Init4
|
||||
d.h[5] = _Init5
|
||||
d.h[6] = _Init6
|
||||
d.h[7] = _Init7
|
||||
d.h[0] = init0
|
||||
d.h[1] = init1
|
||||
d.h[2] = init2
|
||||
d.h[3] = init3
|
||||
d.h[4] = init4
|
||||
d.h[5] = init5
|
||||
d.h[6] = init6
|
||||
d.h[7] = init7
|
||||
} else {
|
||||
d.h[0] = _Init0_224
|
||||
d.h[1] = _Init1_224
|
||||
d.h[2] = _Init2_224
|
||||
d.h[3] = _Init3_224
|
||||
d.h[4] = _Init4_224
|
||||
d.h[5] = _Init5_224
|
||||
d.h[6] = _Init6_224
|
||||
d.h[7] = _Init7_224
|
||||
d.h[0] = init0_224
|
||||
d.h[1] = init1_224
|
||||
d.h[2] = init2_224
|
||||
d.h[3] = init3_224
|
||||
d.h[4] = init4_224
|
||||
d.h[5] = init5_224
|
||||
d.h[6] = init6_224
|
||||
d.h[7] = init7_224
|
||||
}
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
|
|
@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
|
|||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > _Chunk-d.nx {
|
||||
n = _Chunk - d.nx
|
||||
if n > chunk-d.nx {
|
||||
n = chunk - d.nx
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
d.x[d.nx+i] = p[i]
|
||||
}
|
||||
d.nx += n
|
||||
if d.nx == _Chunk {
|
||||
_Block(d, d.x[0:])
|
||||
if d.nx == chunk {
|
||||
block(d, d.x[0:])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := _Block(d, p)
|
||||
p = p[n:]
|
||||
if len(p) >= chunk {
|
||||
n := len(p) &^ (chunk - 1)
|
||||
block(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,11 +75,10 @@ var _K = []uint32{
|
|||
0xc67178f2,
|
||||
}
|
||||
|
||||
func _Block(dig *digest, p []byte) int {
|
||||
func block(dig *digest, p []byte) {
|
||||
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]
|
||||
for len(p) >= _Chunk {
|
||||
for len(p) >= chunk {
|
||||
// Can interlace the computation of w with the
|
||||
// rounds below if needed for speed.
|
||||
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])
|
||||
}
|
||||
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)
|
||||
|
||||
t2 := (w[i-15]>>7 | w[i-15]<<(32-7)) ^ (w[i-15]>>18 | w[i-15]<<(32-18)) ^ (w[i-15] >> 3)
|
||||
|
||||
v1 := w[i-2]
|
||||
t1 := (v1>>17 | v1<<(32-17)) ^ (v1>>19 | v1<<(32-19)) ^ (v1 >> 10)
|
||||
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]
|
||||
}
|
||||
|
||||
|
|
@ -120,10 +119,8 @@ func _Block(dig *digest, p []byte) int {
|
|||
h6 += g
|
||||
h7 += h
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
p = p[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
|
||||
return n
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,29 +26,29 @@ const Size384 = 48
|
|||
const BlockSize = 128
|
||||
|
||||
const (
|
||||
_Chunk = 128
|
||||
_Init0 = 0x6a09e667f3bcc908
|
||||
_Init1 = 0xbb67ae8584caa73b
|
||||
_Init2 = 0x3c6ef372fe94f82b
|
||||
_Init3 = 0xa54ff53a5f1d36f1
|
||||
_Init4 = 0x510e527fade682d1
|
||||
_Init5 = 0x9b05688c2b3e6c1f
|
||||
_Init6 = 0x1f83d9abfb41bd6b
|
||||
_Init7 = 0x5be0cd19137e2179
|
||||
_Init0_384 = 0xcbbb9d5dc1059ed8
|
||||
_Init1_384 = 0x629a292a367cd507
|
||||
_Init2_384 = 0x9159015a3070dd17
|
||||
_Init3_384 = 0x152fecd8f70e5939
|
||||
_Init4_384 = 0x67332667ffc00b31
|
||||
_Init5_384 = 0x8eb44a8768581511
|
||||
_Init6_384 = 0xdb0c2e0d64f98fa7
|
||||
_Init7_384 = 0x47b5481dbefa4fa4
|
||||
chunk = 128
|
||||
init0 = 0x6a09e667f3bcc908
|
||||
init1 = 0xbb67ae8584caa73b
|
||||
init2 = 0x3c6ef372fe94f82b
|
||||
init3 = 0xa54ff53a5f1d36f1
|
||||
init4 = 0x510e527fade682d1
|
||||
init5 = 0x9b05688c2b3e6c1f
|
||||
init6 = 0x1f83d9abfb41bd6b
|
||||
init7 = 0x5be0cd19137e2179
|
||||
init0_384 = 0xcbbb9d5dc1059ed8
|
||||
init1_384 = 0x629a292a367cd507
|
||||
init2_384 = 0x9159015a3070dd17
|
||||
init3_384 = 0x152fecd8f70e5939
|
||||
init4_384 = 0x67332667ffc00b31
|
||||
init5_384 = 0x8eb44a8768581511
|
||||
init6_384 = 0xdb0c2e0d64f98fa7
|
||||
init7_384 = 0x47b5481dbefa4fa4
|
||||
)
|
||||
|
||||
// digest represents the partial evaluation of a checksum.
|
||||
type digest struct {
|
||||
h [8]uint64
|
||||
x [_Chunk]byte
|
||||
x [chunk]byte
|
||||
nx int
|
||||
len uint64
|
||||
is384 bool // mark if this digest is SHA-384
|
||||
|
|
@ -56,23 +56,23 @@ type digest struct {
|
|||
|
||||
func (d *digest) Reset() {
|
||||
if !d.is384 {
|
||||
d.h[0] = _Init0
|
||||
d.h[1] = _Init1
|
||||
d.h[2] = _Init2
|
||||
d.h[3] = _Init3
|
||||
d.h[4] = _Init4
|
||||
d.h[5] = _Init5
|
||||
d.h[6] = _Init6
|
||||
d.h[7] = _Init7
|
||||
d.h[0] = init0
|
||||
d.h[1] = init1
|
||||
d.h[2] = init2
|
||||
d.h[3] = init3
|
||||
d.h[4] = init4
|
||||
d.h[5] = init5
|
||||
d.h[6] = init6
|
||||
d.h[7] = init7
|
||||
} else {
|
||||
d.h[0] = _Init0_384
|
||||
d.h[1] = _Init1_384
|
||||
d.h[2] = _Init2_384
|
||||
d.h[3] = _Init3_384
|
||||
d.h[4] = _Init4_384
|
||||
d.h[5] = _Init5_384
|
||||
d.h[6] = _Init6_384
|
||||
d.h[7] = _Init7_384
|
||||
d.h[0] = init0_384
|
||||
d.h[1] = init1_384
|
||||
d.h[2] = init2_384
|
||||
d.h[3] = init3_384
|
||||
d.h[4] = init4_384
|
||||
d.h[5] = init5_384
|
||||
d.h[6] = init6_384
|
||||
d.h[7] = init7_384
|
||||
}
|
||||
d.nx = 0
|
||||
d.len = 0
|
||||
|
|
@ -107,21 +107,24 @@ func (d *digest) Write(p []byte) (nn int, err error) {
|
|||
d.len += uint64(nn)
|
||||
if d.nx > 0 {
|
||||
n := len(p)
|
||||
if n > _Chunk-d.nx {
|
||||
n = _Chunk - d.nx
|
||||
if n > chunk-d.nx {
|
||||
n = chunk - d.nx
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
d.x[d.nx+i] = p[i]
|
||||
}
|
||||
d.nx += n
|
||||
if d.nx == _Chunk {
|
||||
_Block(d, d.x[0:])
|
||||
if d.nx == chunk {
|
||||
block(d, d.x[0:])
|
||||
d.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := _Block(d, p)
|
||||
p = p[n:]
|
||||
if len(p) >= chunk {
|
||||
n := len(p) &^ (chunk - 1)
|
||||
block(d, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
d.nx = copy(d.x[:], p)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,20 +91,20 @@ var _K = []uint64{
|
|||
0x6c44198c4a475817,
|
||||
}
|
||||
|
||||
func _Block(dig *digest, p []byte) int {
|
||||
func block(dig *digest, p []byte) {
|
||||
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]
|
||||
for len(p) >= _Chunk {
|
||||
for len(p) >= chunk {
|
||||
for i := 0; i < 16; i++ {
|
||||
j := i * 8
|
||||
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])
|
||||
}
|
||||
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)
|
||||
|
||||
t2 := (w[i-15]>>1 | w[i-15]<<(64-1)) ^ (w[i-15]>>8 | w[i-15]<<(64-8)) ^ (w[i-15] >> 7)
|
||||
v1 := w[i-2]
|
||||
t1 := (v1>>19 | v1<<(64-19)) ^ (v1>>61 | v1<<(64-61)) ^ (v1 >> 6)
|
||||
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]
|
||||
}
|
||||
|
|
@ -135,10 +135,8 @@ func _Block(dig *digest, p []byte) int {
|
|||
h6 += g
|
||||
h7 += h
|
||||
|
||||
p = p[_Chunk:]
|
||||
n += _Chunk
|
||||
p = p[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
|
||||
return n
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,9 +55,11 @@ var cipherSuites = []*cipherSuite{
|
|||
{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_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_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_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
|
||||
}
|
||||
|
||||
func cipherRC4(key, iv []byte, isRead bool) interface{} {
|
||||
|
|
@ -182,7 +184,9 @@ const (
|
|||
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
|
||||
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_3DES_EDE_CBC_SHA uint16 = 0xc012
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
|
||||
)
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ const (
|
|||
const (
|
||||
typeClientHello uint8 = 1
|
||||
typeServerHello uint8 = 2
|
||||
typeNewSessionTicket uint8 = 4
|
||||
typeCertificate uint8 = 11
|
||||
typeServerKeyExchange uint8 = 12
|
||||
typeCertificateRequest uint8 = 13
|
||||
|
|
@ -63,6 +64,7 @@ var (
|
|||
extensionStatusRequest uint16 = 5
|
||||
extensionSupportedCurves uint16 = 10
|
||||
extensionSupportedPoints uint16 = 11
|
||||
extensionSessionTicket uint16 = 35
|
||||
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
|
||||
)
|
||||
|
||||
|
|
@ -97,6 +99,7 @@ const (
|
|||
// ConnectionState records basic TLS details about the connection.
|
||||
type ConnectionState struct {
|
||||
HandshakeComplete bool
|
||||
DidResume bool
|
||||
CipherSuite uint16
|
||||
NegotiatedProtocol string
|
||||
NegotiatedProtocolIsMutual bool
|
||||
|
|
@ -180,6 +183,22 @@ type Config struct {
|
|||
// CipherSuites is a list of supported cipher suites. If CipherSuites
|
||||
// is nil, TLS uses a list of suites supported by the implementation.
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ type Conn struct {
|
|||
haveVers bool // version has been negotiated
|
||||
config *Config // configuration passed to constructor
|
||||
handshakeComplete bool
|
||||
didResume bool // whether this connection was a session resumption
|
||||
cipherSuite uint16
|
||||
ocspResponse []byte // stapled OCSP response
|
||||
peerCertificates []*x509.Certificate
|
||||
|
|
@ -44,8 +45,7 @@ type Conn struct {
|
|||
clientProtocolFallback bool
|
||||
|
||||
// first permanent error
|
||||
errMutex sync.Mutex
|
||||
err error
|
||||
connErr
|
||||
|
||||
// input/output
|
||||
in, out halfConn // in.Mutex < out.Mutex
|
||||
|
|
@ -56,21 +56,25 @@ type Conn struct {
|
|||
tmp [16]byte
|
||||
}
|
||||
|
||||
func (c *Conn) setError(err error) error {
|
||||
c.errMutex.Lock()
|
||||
defer c.errMutex.Unlock()
|
||||
type connErr struct {
|
||||
mu sync.Mutex
|
||||
value error
|
||||
}
|
||||
|
||||
if c.err == nil {
|
||||
c.err = err
|
||||
func (e *connErr) setError(err error) error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
if e.value == nil {
|
||||
e.value = err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) error() error {
|
||||
c.errMutex.Lock()
|
||||
defer c.errMutex.Unlock()
|
||||
|
||||
return c.err
|
||||
func (e *connErr) error() error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.value
|
||||
}
|
||||
|
||||
// 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[1] = byte(err.(alert))
|
||||
c.writeRecord(recordTypeAlert, c.tmp[0:2])
|
||||
c.err = &net.OpError{Op: "local error", Err: err}
|
||||
return n, c.err
|
||||
return n, c.setError(&net.OpError{Op: "local error", Err: err})
|
||||
}
|
||||
}
|
||||
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.
|
||||
func (c *Conn) readHandshake() (interface{}, error) {
|
||||
for c.hand.Len() < 4 {
|
||||
if c.err != nil {
|
||||
return nil, c.err
|
||||
if err := c.error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.readRecord(recordTypeHandshake); err != nil {
|
||||
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])
|
||||
if n > maxHandshake {
|
||||
c.sendAlert(alertInternalError)
|
||||
return nil, c.err
|
||||
return nil, c.error()
|
||||
}
|
||||
for c.hand.Len() < 4+n {
|
||||
if c.err != nil {
|
||||
return nil, c.err
|
||||
if err := c.error(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.readRecord(recordTypeHandshake); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -738,12 +741,12 @@ func (c *Conn) readHandshake() (interface{}, error) {
|
|||
|
||||
// Write writes data to the connection.
|
||||
func (c *Conn) Write(b []byte) (int, error) {
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
if err := c.error(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if c.err = c.Handshake(); c.err != nil {
|
||||
return 0, c.err
|
||||
if err := c.Handshake(); err != nil {
|
||||
return 0, c.setError(err)
|
||||
}
|
||||
|
||||
c.out.Lock()
|
||||
|
|
@ -753,9 +756,8 @@ func (c *Conn) Write(b []byte) (int, error) {
|
|||
return 0, alertInternalError
|
||||
}
|
||||
|
||||
var n int
|
||||
n, c.err = c.writeRecord(recordTypeApplicationData, b)
|
||||
return n, c.err
|
||||
n, err := c.writeRecord(recordTypeApplicationData, b)
|
||||
return n, c.setError(err)
|
||||
}
|
||||
|
||||
// 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()
|
||||
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 {
|
||||
// Soft error, like EAGAIN
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if c.err != nil {
|
||||
return 0, c.err
|
||||
if err := c.error(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err = c.input.Read(b)
|
||||
if c.input.off >= len(c.input.data) {
|
||||
|
|
@ -829,6 +831,7 @@ func (c *Conn) ConnectionState() ConnectionState {
|
|||
state.HandshakeComplete = c.handshakeComplete
|
||||
if c.handshakeComplete {
|
||||
state.NegotiatedProtocol = c.clientProtocol
|
||||
state.DidResume = c.didResume
|
||||
state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
|
||||
state.CipherSuite = c.cipherSuite
|
||||
state.PeerCertificates = c.peerCertificates
|
||||
|
|
|
|||
|
|
@ -278,8 +278,9 @@ func (c *Conn) clientHandshake() error {
|
|||
c.writeRecord(recordTypeHandshake, certVerify.marshal())
|
||||
}
|
||||
|
||||
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
|
||||
keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
|
||||
masterSecret := masterFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random)
|
||||
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 */)
|
||||
clientHash := suite.mac(c.vers, clientMAC)
|
||||
|
|
@ -306,8 +307,8 @@ func (c *Conn) clientHandshake() error {
|
|||
serverHash := suite.mac(c.vers, serverMAC)
|
||||
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
|
||||
c.readRecord(recordTypeChangeCipherSpec)
|
||||
if c.err != nil {
|
||||
return c.err
|
||||
if err := c.error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err = c.readHandshake()
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"flag"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -39,7 +40,15 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config *
|
|||
}
|
||||
|
||||
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")
|
||||
|
|
@ -49,25 +58,33 @@ func TestRunClient(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
testConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}
|
||||
|
||||
conn, err := Dial("tcp", "127.0.0.1:10443", testConfig)
|
||||
tcpConn, err := net.Dial("tcp", "127.0.0.1:10443")
|
||||
if err != nil {
|
||||
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.Close()
|
||||
|
||||
record.WriteTo(os.Stdout)
|
||||
}
|
||||
|
||||
// Script of interaction with gnutls implementation.
|
||||
// The values for this test are obtained by building and running in client mode:
|
||||
// % go test -run "TestRunClient" -connect
|
||||
// and then:
|
||||
// % 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
|
||||
// % go test -test.run "TestRunClient" -connect
|
||||
// The recorded bytes are written to stdout.
|
||||
//
|
||||
// Where key.pem is:
|
||||
// The server private key is:
|
||||
// -----BEGIN RSA PRIVATE KEY-----
|
||||
// MIIBPAIBAAJBAJ+zw4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVD
|
||||
// TGiXav6ooKXfX3j/7tdkuD8Ey2//Kv7+ue0CAwEAAQJAN6W31vDEP2DjdqhzCDDu
|
||||
|
|
@ -78,17 +95,20 @@ func TestRunClient(t *testing.T) {
|
|||
// vnlEGo8K85u+KwIOimM48ZG8oTk7iFdkqLJR1utT3aU=
|
||||
// -----END RSA PRIVATE KEY-----
|
||||
//
|
||||
// and cert.pem is:
|
||||
// and certificate is:
|
||||
// -----BEGIN CERTIFICATE-----
|
||||
// MIIBoDCCAUoCAQAwDQYJKoZIhvcNAQEEBQAwYzELMAkGA1UEBhMCQVUxEzARBgNV
|
||||
// BAgTClF1ZWVuc2xhbmQxGjAYBgNVBAoTEUNyeXB0U29mdCBQdHkgTHRkMSMwIQYD
|
||||
// VQQDExpTZXJ2ZXIgdGVzdCBjZXJ0ICg1MTIgYml0KTAeFw05NzA5MDkwMzQxMjZa
|
||||
// Fw05NzEwMDkwMzQxMjZaMF4xCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
|
||||
// YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFzAVBgNVBAMT
|
||||
// DkVyaWMgdGhlIFlvdW5nMFEwCQYFKw4DAgwFAANEAAJBALVEqPODnpI4rShlY8S7
|
||||
// tB713JNvabvn6Gned7zylwLLiXQAo/PAT6mfdWPTyCX9RlId/Aroh1ou893BA32Q
|
||||
// sggwDQYJKoZIhvcNAQEEBQADQQCU5SSgapJSdRXJoX+CpCvFy+JVh9HpSjCpSNKO
|
||||
// 19raHv98hKAUJuP9HyM+SUsffO6mAIgitUaqW8/wDMePhEC3
|
||||
// MIICKzCCAdWgAwIBAgIJALE1E2URIMWSMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
|
||||
// BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
||||
// aWRnaXRzIFB0eSBMdGQwHhcNMTIwNDA2MTcxMDEzWhcNMTUwNDA2MTcxMDEzWjBF
|
||||
// MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
|
||||
// ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ+z
|
||||
// w4Qnlf8SMVIPFe9GEcStgOY2Ww/dgNdhjeD8ckUJNP5VZkVDTGiXav6ooKXfX3j/
|
||||
// 7tdkuD8Ey2//Kv7+ue0CAwEAAaOBpzCBpDAdBgNVHQ4EFgQUeKaXmmO1xaGlM7oi
|
||||
// fCNuWxt6zCswdQYDVR0jBG4wbIAUeKaXmmO1xaGlM7oifCNuWxt6zCuhSaRHMEUx
|
||||
// CzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRl
|
||||
// cm5ldCBXaWRnaXRzIFB0eSBMdGSCCQCxNRNlESDFkjAMBgNVHRMEBTADAQH/MA0G
|
||||
// CSqGSIb3DQEBBQUAA0EAhTZAc8G7GtrUWZ8tonAxRnTsg26oyDxRrzms7EC86CJG
|
||||
// HZnWRiok1IsFCEv7NRFukrt3uuQSu/TIXpyBqJdgTA==
|
||||
// -----END CERTIFICATE-----
|
||||
var rc4ClientScript = [][]byte{
|
||||
{
|
||||
|
|
@ -210,3 +230,163 @@ var rc4ClientScript = [][]byte{
|
|||
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,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ type clientHelloMsg struct {
|
|||
ocspStapling bool
|
||||
supportedCurves []uint16
|
||||
supportedPoints []uint8
|
||||
ticketSupported bool
|
||||
sessionTicket []uint8
|
||||
}
|
||||
|
||||
func (m *clientHelloMsg) equal(i interface{}) bool {
|
||||
|
|
@ -36,7 +38,9 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
|
|||
m.serverName == m1.serverName &&
|
||||
m.ocspStapling == m1.ocspStapling &&
|
||||
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 {
|
||||
|
|
@ -66,6 +70,10 @@ func (m *clientHelloMsg) marshal() []byte {
|
|||
extensionsLength += 1 + len(m.supportedPoints)
|
||||
numExtensions++
|
||||
}
|
||||
if m.ticketSupported {
|
||||
extensionsLength += len(m.sessionTicket)
|
||||
numExtensions++
|
||||
}
|
||||
if numExtensions > 0 {
|
||||
extensionsLength += 4 * numExtensions
|
||||
length += 2 + extensionsLength
|
||||
|
|
@ -180,6 +188,17 @@ func (m *clientHelloMsg) marshal() []byte {
|
|||
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
|
||||
|
||||
|
|
@ -228,6 +247,8 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
|
|||
m.nextProtoNeg = false
|
||||
m.serverName = ""
|
||||
m.ocspStapling = false
|
||||
m.ticketSupported = false
|
||||
m.sessionTicket = nil
|
||||
|
||||
if len(data) == 0 {
|
||||
// ClientHello is optionally followed by extension data
|
||||
|
|
@ -311,6 +332,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
|
|||
}
|
||||
m.supportedPoints = make([]uint8, l)
|
||||
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:]
|
||||
}
|
||||
|
|
@ -328,6 +353,7 @@ type serverHelloMsg struct {
|
|||
nextProtoNeg bool
|
||||
nextProtos []string
|
||||
ocspStapling bool
|
||||
ticketSupported bool
|
||||
}
|
||||
|
||||
func (m *serverHelloMsg) equal(i interface{}) bool {
|
||||
|
|
@ -344,7 +370,8 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
|
|||
m.compressionMethod == m1.compressionMethod &&
|
||||
m.nextProtoNeg == m1.nextProtoNeg &&
|
||||
eqStrings(m.nextProtos, m1.nextProtos) &&
|
||||
m.ocspStapling == m1.ocspStapling
|
||||
m.ocspStapling == m1.ocspStapling &&
|
||||
m.ticketSupported == m1.ticketSupported
|
||||
}
|
||||
|
||||
func (m *serverHelloMsg) marshal() []byte {
|
||||
|
|
@ -368,6 +395,9 @@ func (m *serverHelloMsg) marshal() []byte {
|
|||
if m.ocspStapling {
|
||||
numExtensions++
|
||||
}
|
||||
if m.ticketSupported {
|
||||
numExtensions++
|
||||
}
|
||||
if numExtensions > 0 {
|
||||
extensionsLength += 4 * numExtensions
|
||||
length += 2 + extensionsLength
|
||||
|
|
@ -416,6 +446,11 @@ func (m *serverHelloMsg) marshal() []byte {
|
|||
z[1] = byte(extensionStatusRequest)
|
||||
z = z[4:]
|
||||
}
|
||||
if m.ticketSupported {
|
||||
z[0] = byte(extensionSessionTicket >> 8)
|
||||
z[1] = byte(extensionSessionTicket)
|
||||
z = z[4:]
|
||||
}
|
||||
|
||||
m.raw = x
|
||||
|
||||
|
|
@ -445,6 +480,7 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
|
|||
m.nextProtoNeg = false
|
||||
m.nextProtos = nil
|
||||
m.ocspStapling = false
|
||||
m.ticketSupported = false
|
||||
|
||||
if len(data) == 0 {
|
||||
// ServerHello is optionally followed by extension data
|
||||
|
|
@ -474,14 +510,14 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
|
|||
switch extension {
|
||||
case extensionNextProtoNeg:
|
||||
m.nextProtoNeg = true
|
||||
d := data
|
||||
d := data[:length]
|
||||
for len(d) > 0 {
|
||||
l := int(d[0])
|
||||
d = d[1:]
|
||||
if l == 0 || l > len(d) {
|
||||
return false
|
||||
}
|
||||
m.nextProtos = append(m.nextProtos, string(d[0:l]))
|
||||
m.nextProtos = append(m.nextProtos, string(d[:l]))
|
||||
d = d[l:]
|
||||
}
|
||||
case extensionStatusRequest:
|
||||
|
|
@ -489,6 +525,11 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
|
|||
return false
|
||||
}
|
||||
m.ocspStapling = true
|
||||
case extensionSessionTicket:
|
||||
if length > 0 {
|
||||
return false
|
||||
}
|
||||
m.ticketSupported = true
|
||||
}
|
||||
data = data[length:]
|
||||
}
|
||||
|
|
@ -1030,6 +1071,65 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool {
|
|||
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 {
|
||||
if len(x) != len(y) {
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ var tests = []interface{}{
|
|||
&certificateStatusMsg{},
|
||||
&clientKeyExchangeMsg{},
|
||||
&nextProtoMsg{},
|
||||
&newSessionTicketMsg{},
|
||||
&sessionState{},
|
||||
}
|
||||
|
||||
type testMessage interface {
|
||||
|
|
@ -127,6 +129,12 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
|
|||
for i := range m.supportedCurves {
|
||||
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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
@ -207,3 +222,22 @@ func (*nextProtoMsg) Generate(rand *rand.Rand, size int) reflect.Value {
|
|||
m.proto = randomString(rand.Intn(255), rand)
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,31 +13,120 @@ import (
|
|||
"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 {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
clientHello, ok := msg.(*clientHelloMsg)
|
||||
if !ok {
|
||||
return c.sendAlert(alertUnexpectedMessage)
|
||||
|
||||
// For an overview of TLS handshaking, see https://tools.ietf.org/html/rfc5246#section-7.3
|
||||
if isResume {
|
||||
// The client has included a session ticket and so we do an abbreviated handshake.
|
||||
if err := hs.doResumeHandshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.establishKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
vers, ok := mutualVersion(clientHello.vers)
|
||||
if !ok {
|
||||
return c.sendAlert(alertProtocolVersion)
|
||||
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
|
||||
|
||||
finishedHash := newFinishedHash(vers)
|
||||
finishedHash.Write(clientHello.marshal())
|
||||
hs.finishedHash = newFinishedHash(c.vers)
|
||||
hs.finishedHash.Write(hs.clientHello.marshal())
|
||||
|
||||
hello := new(serverHelloMsg)
|
||||
hs.hello = new(serverHelloMsg)
|
||||
|
||||
supportedCurve := false
|
||||
Curves:
|
||||
for _, curve := range clientHello.supportedCurves {
|
||||
for _, curve := range hs.clientHello.supportedCurves {
|
||||
switch curve {
|
||||
case curveP256, curveP384, curveP521:
|
||||
supportedCurve = true
|
||||
|
|
@ -46,110 +135,173 @@ Curves:
|
|||
}
|
||||
|
||||
supportedPointFormat := false
|
||||
for _, pointFormat := range clientHello.supportedPoints {
|
||||
for _, pointFormat := range hs.clientHello.supportedPoints {
|
||||
if pointFormat == pointFormatUncompressed {
|
||||
supportedPointFormat = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
hs.ellipticOk = supportedCurve && supportedPointFormat
|
||||
|
||||
foundCompression := false
|
||||
// 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 {
|
||||
foundCompression = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if suite == nil || !foundCompression {
|
||||
return c.sendAlert(alertHandshakeFailure)
|
||||
if !foundCompression {
|
||||
return false, c.sendAlert(alertHandshakeFailure)
|
||||
}
|
||||
|
||||
hello.vers = vers
|
||||
hello.cipherSuite = suite.id
|
||||
hs.hello.vers = c.vers
|
||||
t := uint32(config.time().Unix())
|
||||
hello.random = make([]byte, 32)
|
||||
hello.random[0] = byte(t >> 24)
|
||||
hello.random[1] = byte(t >> 16)
|
||||
hello.random[2] = byte(t >> 8)
|
||||
hello.random[3] = byte(t)
|
||||
_, err = io.ReadFull(config.rand(), hello.random[4:])
|
||||
hs.hello.random = make([]byte, 32)
|
||||
hs.hello.random[0] = byte(t >> 24)
|
||||
hs.hello.random[1] = byte(t >> 16)
|
||||
hs.hello.random[2] = byte(t >> 8)
|
||||
hs.hello.random[3] = byte(t)
|
||||
_, err = io.ReadFull(config.rand(), hs.hello.random[4:])
|
||||
if err != nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
return false, c.sendAlert(alertInternalError)
|
||||
}
|
||||
hello.compressionMethod = compressionNone
|
||||
if clientHello.nextProtoNeg {
|
||||
hello.nextProtoNeg = true
|
||||
hello.nextProtos = config.NextProtos
|
||||
hs.hello.compressionMethod = compressionNone
|
||||
if len(hs.clientHello.serverName) > 0 {
|
||||
c.serverName = hs.clientHello.serverName
|
||||
}
|
||||
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 {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
cert := &config.Certificates[0]
|
||||
if len(clientHello.serverName) > 0 {
|
||||
c.serverName = clientHello.serverName
|
||||
cert = config.getCertificateForName(clientHello.serverName)
|
||||
if len(hs.clientHello.serverName) > 0 {
|
||||
cert = config.getCertificateForName(hs.clientHello.serverName)
|
||||
}
|
||||
|
||||
if clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
|
||||
hello.ocspStapling = true
|
||||
if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 {
|
||||
hs.hello.ocspStapling = true
|
||||
}
|
||||
|
||||
finishedHash.Write(hello.marshal())
|
||||
c.writeRecord(recordTypeHandshake, hello.marshal())
|
||||
hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled
|
||||
hs.hello.cipherSuite = hs.suite.id
|
||||
hs.finishedHash.Write(hs.hello.marshal())
|
||||
c.writeRecord(recordTypeHandshake, hs.hello.marshal())
|
||||
|
||||
certMsg := new(certificateMsg)
|
||||
certMsg.certificates = cert.Certificate
|
||||
finishedHash.Write(certMsg.marshal())
|
||||
hs.finishedHash.Write(certMsg.marshal())
|
||||
c.writeRecord(recordTypeHandshake, certMsg.marshal())
|
||||
|
||||
if hello.ocspStapling {
|
||||
if hs.hello.ocspStapling {
|
||||
certStatus := new(certificateStatusMsg)
|
||||
certStatus.statusType = statusTypeOCSP
|
||||
certStatus.response = cert.OCSPStaple
|
||||
finishedHash.Write(certStatus.marshal())
|
||||
hs.finishedHash.Write(certStatus.marshal())
|
||||
c.writeRecord(recordTypeHandshake, certStatus.marshal())
|
||||
}
|
||||
|
||||
keyAgreement := suite.ka()
|
||||
skx, err := keyAgreement.generateServerKeyExchange(config, cert, clientHello, hello)
|
||||
keyAgreement := hs.suite.ka()
|
||||
skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello)
|
||||
if err != nil {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return err
|
||||
}
|
||||
if skx != nil {
|
||||
finishedHash.Write(skx.marshal())
|
||||
hs.finishedHash.Write(skx.marshal())
|
||||
c.writeRecord(recordTypeHandshake, skx.marshal())
|
||||
}
|
||||
|
||||
|
|
@ -166,28 +318,29 @@ FindCipherSuite:
|
|||
if config.ClientCAs != nil {
|
||||
certReq.certificateAuthorities = config.ClientCAs.Subjects()
|
||||
}
|
||||
finishedHash.Write(certReq.marshal())
|
||||
hs.finishedHash.Write(certReq.marshal())
|
||||
c.writeRecord(recordTypeHandshake, certReq.marshal())
|
||||
}
|
||||
|
||||
helloDone := new(serverHelloDoneMsg)
|
||||
finishedHash.Write(helloDone.marshal())
|
||||
hs.finishedHash.Write(helloDone.marshal())
|
||||
c.writeRecord(recordTypeHandshake, helloDone.marshal())
|
||||
|
||||
var pub *rsa.PublicKey // public key for client auth, if any
|
||||
|
||||
msg, err = c.readHandshake()
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
// If we requested a client certificate, then the client must send a
|
||||
// certificate message, even if it's empty.
|
||||
if config.ClientAuth >= RequestClientCert {
|
||||
if certMsg, ok = msg.(*certificateMsg); !ok {
|
||||
return c.sendAlert(alertHandshakeFailure)
|
||||
}
|
||||
finishedHash.Write(certMsg.marshal())
|
||||
hs.finishedHash.Write(certMsg.marshal())
|
||||
|
||||
if len(certMsg.certificates) == 0 {
|
||||
// The client didn't actually send a certificate
|
||||
|
|
@ -198,54 +351,9 @@ FindCipherSuite:
|
|||
}
|
||||
}
|
||||
|
||||
certs := make([]*x509.Certificate, len(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 {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
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
|
||||
pub, err = hs.processCertsFromClient(certMsg.certificates)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err = c.readHandshake()
|
||||
|
|
@ -259,7 +367,7 @@ FindCipherSuite:
|
|||
if !ok {
|
||||
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,
|
||||
// the client will send us a certificateVerifyMsg immediately after the
|
||||
|
|
@ -278,15 +386,15 @@ FindCipherSuite:
|
|||
}
|
||||
|
||||
digest := make([]byte, 0, 36)
|
||||
digest = finishedHash.serverMD5.Sum(digest)
|
||||
digest = finishedHash.serverSHA1.Sum(digest)
|
||||
digest = hs.finishedHash.serverMD5.Sum(digest)
|
||||
digest = hs.finishedHash.serverSHA1.Sum(digest)
|
||||
err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature)
|
||||
if err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
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)
|
||||
|
|
@ -294,20 +402,38 @@ FindCipherSuite:
|
|||
c.sendAlert(alertHandshakeFailure)
|
||||
return err
|
||||
}
|
||||
hs.masterSecret = masterFromPreMasterSecret(c.vers, preMasterSecret, hs.clientHello.random, hs.hello.random)
|
||||
|
||||
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
|
||||
keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen)
|
||||
return nil
|
||||
}
|
||||
|
||||
clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */)
|
||||
clientHash := suite.mac(c.vers, clientMAC)
|
||||
func (hs *serverHandshakeState) establishKeys() error {
|
||||
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)
|
||||
|
||||
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)
|
||||
if err := c.error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hello.nextProtoNeg {
|
||||
msg, err = c.readHandshake()
|
||||
if hs.hello.nextProtoNeg {
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -315,11 +441,11 @@ FindCipherSuite:
|
|||
if !ok {
|
||||
return c.sendAlert(alertUnexpectedMessage)
|
||||
}
|
||||
finishedHash.Write(nextProto.marshal())
|
||||
hs.finishedHash.Write(nextProto.marshal())
|
||||
c.clientProtocol = nextProto.proto
|
||||
}
|
||||
|
||||
msg, err = c.readHandshake()
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -328,25 +454,142 @@ FindCipherSuite:
|
|||
return c.sendAlert(alertUnexpectedMessage)
|
||||
}
|
||||
|
||||
verify := finishedHash.clientSum(masterSecret)
|
||||
verify := hs.finishedHash.clientSum(hs.masterSecret)
|
||||
if len(verify) != len(clientFinished.verifyData) ||
|
||||
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
|
||||
return c.sendAlert(alertHandshakeFailure)
|
||||
}
|
||||
|
||||
finishedHash.Write(clientFinished.marshal())
|
||||
hs.finishedHash.Write(clientFinished.marshal())
|
||||
return nil
|
||||
}
|
||||
|
||||
serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */)
|
||||
serverHash := suite.mac(c.vers, serverMAC)
|
||||
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
|
||||
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
||||
func (hs *serverHandshakeState) sendSessionTicket() error {
|
||||
if !hs.hello.ticketSupported {
|
||||
return nil
|
||||
}
|
||||
|
||||
finished := new(finishedMsg)
|
||||
finished.verifyData = finishedHash.serverSum(masterSecret)
|
||||
c.writeRecord(recordTypeHandshake, finished.marshal())
|
||||
c := hs.c
|
||||
m := new(newSessionTicketMsg)
|
||||
|
||||
c.handshakeComplete = true
|
||||
c.cipherSuite = suite.id
|
||||
var err error
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,15 @@ import (
|
|||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -80,13 +83,20 @@ func TestRejectBadProtocolVersion(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)
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -186,6 +196,11 @@ func TestHandshakeServerSNI(t *testing.T) {
|
|||
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 {
|
||||
name string
|
||||
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 testCipherSuites = flag.String("ciphersuites",
|
||||
"0x"+strconv.FormatInt(int64(TLS_RSA_WITH_RC4_128_SHA), 16),
|
||||
"cipher suites to accept in serving mode")
|
||||
var testClientAuth = flag.Int("clientauth", 0, "value for tls.Config.ClientAuth")
|
||||
|
||||
func TestRunServer(t *testing.T) {
|
||||
if !*serve {
|
||||
return
|
||||
}
|
||||
|
||||
func GetTestConfig() *Config {
|
||||
var config = *testConfig
|
||||
suites := strings.Split(*testCipherSuites, ",")
|
||||
testConfig.CipherSuites = make([]uint16, len(suites))
|
||||
config.CipherSuites = make([]uint16, len(suites))
|
||||
for i := range suites {
|
||||
suite, err := strconv.ParseUint(suites[i], 0, 64)
|
||||
if err != nil {
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
log.Printf("Now listening for connections on %s", addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
tcpConn, err := l.Accept()
|
||||
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)
|
||||
break
|
||||
}
|
||||
|
||||
_, err = c.Write([]byte("hello, world\n"))
|
||||
_, err = conn.Write([]byte("hello, world\n"))
|
||||
if err != nil {
|
||||
log.Printf("error from TLS: %s", err)
|
||||
log.Printf("error from Write: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
st := c.(*Conn).ConnectionState()
|
||||
if len(st.PeerCertificates) > 0 {
|
||||
log.Print("Handling request from client ", st.PeerCertificates[0].Subject.CommonName)
|
||||
} else {
|
||||
log.Print("Handling request from anon client")
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
c.Close()
|
||||
record.WriteTo(os.Stdout)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -296,10 +389,8 @@ func loadPEMCert(in string) *x509.Certificate {
|
|||
|
||||
// Script of interaction with gnutls implementation.
|
||||
// The values for this test are obtained by building and running in server mode:
|
||||
// % go test -run "TestRunServer" -serve
|
||||
// and then:
|
||||
// % gnutls-cli --insecure --debug 100 -p 10443 localhost > /tmp/log 2>&1
|
||||
// % python parse-gnutls-cli-debug-log.py < /tmp/log
|
||||
// % go test -test.run "TestRunServer" -serve
|
||||
// The recorded bytes are written to stdout.
|
||||
var rc4ServerScript = [][]byte{
|
||||
{
|
||||
0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00,
|
||||
|
|
@ -961,21 +1052,37 @@ var sslv3ServerScript = [][]byte{
|
|||
|
||||
var selectCertificateBySNIScript = [][]byte{
|
||||
{
|
||||
0x16, 0x03, 0x01, 0x00, 0x6e, 0x01, 0x00, 0x00,
|
||||
0x6a, 0x03, 0x01, 0x4f, 0x85, 0xc4, 0xc2, 0xb9,
|
||||
0x39, 0x80, 0x91, 0x66, 0x65, 0x56, 0x8e, 0xdd,
|
||||
0x48, 0xe9, 0xca, 0x34, 0x02, 0x3c, 0xaf, 0x0d,
|
||||
0x73, 0xb5, 0x2a, 0x05, 0x6e, 0xbd, 0x5e, 0x8f,
|
||||
0x38, 0xf9, 0xe5, 0x00, 0x00, 0x28, 0x00, 0x39,
|
||||
0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
|
||||
0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
|
||||
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, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00,
|
||||
0x0e, 0x00, 0x00, 0x0b, 0x73, 0x6e, 0x69, 0x74,
|
||||
0x65, 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00,
|
||||
0x23, 0x00, 0x00,
|
||||
0x16, 0x03, 0x01, 0x00, 0xed, 0x01, 0x00, 0x00,
|
||||
0xe9, 0x03, 0x02, 0x50, 0x5a, 0x1c, 0x90, 0x2b,
|
||||
0xc8, 0xf1, 0xd9, 0x4b, 0xd0, 0x18, 0x69, 0xed,
|
||||
0x5a, 0xbd, 0x68, 0xf6, 0xf7, 0xe3, 0xf0, 0x6e,
|
||||
0xd1, 0xcc, 0xf1, 0x2d, 0x94, 0xa4, 0x01, 0x63,
|
||||
0x91, 0xbe, 0xd0, 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,
|
||||
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,
|
||||
|
|
@ -1053,45 +1160,323 @@ var selectCertificateBySNIScript = [][]byte{
|
|||
},
|
||||
{
|
||||
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
|
||||
0x82, 0x00, 0x80, 0x70, 0x1d, 0x34, 0x75, 0xa2,
|
||||
0xe7, 0xe3, 0x2f, 0x3d, 0xc1, 0x1d, 0xca, 0x0b,
|
||||
0xe3, 0x64, 0xb9, 0x1a, 0x00, 0x69, 0xc4, 0x14,
|
||||
0x05, 0x07, 0x7e, 0xc3, 0x51, 0x43, 0x52, 0x66,
|
||||
0xe3, 0xbd, 0xff, 0x1b, 0x1a, 0x6a, 0x84, 0xf2,
|
||||
0x07, 0x24, 0xd7, 0x12, 0xa8, 0x58, 0xcf, 0x8a,
|
||||
0x50, 0x30, 0xe8, 0xc8, 0xb2, 0xf9, 0x58, 0x1c,
|
||||
0x56, 0x53, 0x76, 0x21, 0xe0, 0x03, 0x7f, 0x77,
|
||||
0xa7, 0xf1, 0xad, 0x67, 0xd4, 0xe2, 0x8f, 0xa0,
|
||||
0x58, 0x6c, 0xe0, 0x28, 0x59, 0xf3, 0xd1, 0x53,
|
||||
0x2b, 0x21, 0xbd, 0xa3, 0x84, 0x31, 0x73, 0xbf,
|
||||
0x84, 0x0f, 0x83, 0xf4, 0xc4, 0xd0, 0xe5, 0x3c,
|
||||
0x2d, 0x3e, 0xf2, 0x8a, 0x1e, 0xe7, 0xe9, 0x1f,
|
||||
0x12, 0x13, 0xad, 0x29, 0xd6, 0x0c, 0xc7, 0xc6,
|
||||
0x05, 0x53, 0x7d, 0x5e, 0xc6, 0x92, 0x72, 0xba,
|
||||
0xd2, 0x93, 0x8f, 0x53, 0x84, 0x87, 0x44, 0x05,
|
||||
0x9f, 0x5d, 0x66, 0x14, 0x03, 0x01, 0x00, 0x01,
|
||||
0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xfc, 0x71,
|
||||
0xaa, 0xa8, 0x37, 0xa8, 0xbd, 0x63, 0xb7, 0xbc,
|
||||
0x95, 0xef, 0x0c, 0xcf, 0x39, 0x31, 0x93, 0xe6,
|
||||
0x86, 0xbd, 0x3f, 0x56, 0x9d, 0xf0, 0xb2, 0xb5,
|
||||
0xd1, 0xa7, 0xc6, 0x45, 0x89, 0x18, 0xfb, 0xa0,
|
||||
0x7f, 0xc1,
|
||||
0x82, 0x00, 0x80, 0x45, 0x6d, 0x68, 0x61, 0xb9,
|
||||
0x1a, 0xe5, 0xeb, 0x67, 0x22, 0x3b, 0x87, 0x19,
|
||||
0x52, 0x86, 0x31, 0x91, 0xee, 0xcd, 0x17, 0x75,
|
||||
0xc6, 0x44, 0xaf, 0x23, 0xef, 0xd9, 0xfa, 0xd2,
|
||||
0x0b, 0xa2, 0xbb, 0xbf, 0x8b, 0x4b, 0x34, 0x50,
|
||||
0xf6, 0x2e, 0x05, 0x09, 0x7e, 0xbf, 0xb3, 0xa6,
|
||||
0x10, 0xe3, 0xc3, 0x49, 0x55, 0xa8, 0xdf, 0x6c,
|
||||
0xaa, 0xab, 0x11, 0x4c, 0x80, 0x0a, 0x45, 0xf8,
|
||||
0x37, 0xbb, 0xd3, 0x18, 0x4e, 0xec, 0x51, 0xbf,
|
||||
0x1a, 0xf6, 0x11, 0x1b, 0xcf, 0x2c, 0xaf, 0x5f,
|
||||
0x0b, 0x52, 0x4e, 0x92, 0x0c, 0x7a, 0xb2, 0x5d,
|
||||
0xe2, 0x1f, 0x83, 0xbe, 0xf5, 0xbf, 0x05, 0xbf,
|
||||
0x99, 0xd6, 0x9c, 0x86, 0x47, 0x5e, 0xb4, 0xff,
|
||||
0xe7, 0xac, 0xad, 0x1e, 0x3c, 0xaa, 0x91, 0x39,
|
||||
0xca, 0xad, 0xc5, 0x54, 0x64, 0x7e, 0xc2, 0x8a,
|
||||
0x48, 0xee, 0xb6, 0x4e, 0xf9, 0x33, 0x82, 0x52,
|
||||
0xe8, 0xed, 0x48, 0x14, 0x03, 0x01, 0x00, 0x01,
|
||||
0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0xc1, 0x2f,
|
||||
0x34, 0x03, 0x2a, 0xf2, 0xfd, 0x83, 0x69, 0x23,
|
||||
0x8c, 0x9e, 0x66, 0x3b, 0xbb, 0xd1, 0xab, 0xbb,
|
||||
0x51, 0x89, 0x27, 0x88, 0x0f, 0x08, 0x3e, 0x00,
|
||||
0xdc, 0xc7, 0x47, 0x82, 0x13, 0x34, 0xec, 0xca,
|
||||
0x68, 0x6a,
|
||||
},
|
||||
{
|
||||
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
|
||||
0x01, 0x00, 0x24, 0xb8, 0x6d, 0x9a, 0x90, 0x3c,
|
||||
0x45, 0xe0, 0xff, 0x63, 0xba, 0xab, 0x3d, 0x7a,
|
||||
0xa6, 0x49, 0x5a, 0x13, 0xdc, 0x0e, 0xa3, 0xba,
|
||||
0x7f, 0x04, 0x19, 0x45, 0xfd, 0xfb, 0xbd, 0x00,
|
||||
0xa3, 0xa7, 0x78, 0x81, 0x38, 0x9f, 0x10, 0x17,
|
||||
0x03, 0x01, 0x00, 0x21, 0x43, 0xc3, 0x91, 0xb7,
|
||||
0xbf, 0x50, 0x0b, 0x04, 0xb4, 0x5d, 0xc6, 0x20,
|
||||
0x64, 0xb8, 0x01, 0x09, 0x25, 0x2c, 0x03, 0x30,
|
||||
0xc0, 0x77, 0xc9, 0x5e, 0xe6, 0xe0, 0x99, 0xdc,
|
||||
0xcd, 0x75, 0x9d, 0x51, 0x82, 0x15, 0x03, 0x01,
|
||||
0x00, 0x16, 0x2d, 0x7a, 0x89, 0x7b, 0x36, 0x85,
|
||||
0x2a, 0x93, 0xcb, 0x83, 0xa7, 0x2f, 0x9e, 0x91,
|
||||
0xfc, 0xad, 0x57, 0xca, 0xf5, 0xbc, 0x13, 0x2f,
|
||||
0x01, 0x00, 0x24, 0xda, 0x61, 0x76, 0x9f, 0x7a,
|
||||
0x8a, 0xd0, 0x5f, 0x9b, 0x3d, 0xa7, 0xd5, 0xdd,
|
||||
0x95, 0x4b, 0xd4, 0x64, 0x2d, 0x2d, 0x6a, 0x98,
|
||||
0x9e, 0xfe, 0x77, 0x76, 0xe3, 0x02, 0x05, 0x0c,
|
||||
0xb2, 0xa6, 0x15, 0x82, 0x28, 0x25, 0xc5, 0x17,
|
||||
0x03, 0x01, 0x00, 0x21, 0x4e, 0x66, 0x2d, 0x50,
|
||||
0x00, 0xa2, 0x44, 0x4d, 0xee, 0x5f, 0x81, 0x67,
|
||||
0x21, 0x5d, 0x94, 0xc0, 0xfb, 0xdc, 0xbd, 0xf6,
|
||||
0xa8, 0x32, 0x8e, 0x2c, 0x22, 0x58, 0x37, 0xb6,
|
||||
0xa3, 0x1e, 0xf8, 0xdd, 0x83, 0x15, 0x03, 0x01,
|
||||
0x00, 0x16, 0x68, 0x3b, 0x3a, 0xd0, 0x1e, 0xc4,
|
||||
0x5e, 0x97, 0x6a, 0x47, 0x38, 0xfe, 0x17, 0x8e,
|
||||
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,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,10 +106,9 @@ var keyExpansionLabel = []byte("key expansion")
|
|||
var clientFinishedLabel = []byte("client finished")
|
||||
var serverFinishedLabel = []byte("server finished")
|
||||
|
||||
// keysFromPreMasterSecret generates the connection keys from the pre master
|
||||
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
|
||||
// RFC 2246, section 6.3.
|
||||
func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
|
||||
// masterFromPreMasterSecret generates the master secret from the pre-master
|
||||
// secret. See http://tools.ietf.org/html/rfc5246#section-8.1
|
||||
func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []byte {
|
||||
prf := pRF10
|
||||
if version == versionSSL30 {
|
||||
prf = pRF30
|
||||
|
|
@ -118,9 +117,21 @@ func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serv
|
|||
var seed [tlsRandomLength * 2]byte
|
||||
copy(seed[0:len(clientRandom)], clientRandom)
|
||||
copy(seed[len(clientRandom):], serverRandom)
|
||||
masterSecret = make([]byte, masterSecretLength)
|
||||
masterSecret := make([]byte, masterSecretLength)
|
||||
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[len(serverRandom):], clientRandom)
|
||||
|
||||
|
|
|
|||
|
|
@ -48,18 +48,23 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
|
|||
in, _ := hex.DecodeString(test.preMasterSecret)
|
||||
clientRandom, _ := hex.DecodeString(test.clientRandom)
|
||||
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)
|
||||
serverMACString := hex.EncodeToString(serverMAC)
|
||||
clientKeyString := hex.EncodeToString(clientKey)
|
||||
serverKeyString := hex.EncodeToString(serverKey)
|
||||
if masterString != test.masterSecret ||
|
||||
clientMACString != test.clientMAC ||
|
||||
if clientMACString != test.clientMAC ||
|
||||
serverMACString != test.serverMAC ||
|
||||
clientKeyString != test.clientKey ||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -146,10 +146,16 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
|
|||
return
|
||||
}
|
||||
|
||||
keyDERBlock, _ := pem.Decode(keyPEMBlock)
|
||||
if keyDERBlock == nil {
|
||||
err = errors.New("crypto/tls: failed to parse key PEM data")
|
||||
return
|
||||
var keyDERBlock *pem.Block
|
||||
for {
|
||||
keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
|
||||
if keyDERBlock == nil {
|
||||
err = errors.New("crypto/tls: failed to parse key PEM data")
|
||||
return
|
||||
}
|
||||
if keyDERBlock.Type != "CERTIFICATE" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// OpenSSL 0.9.8 generates PKCS#1 private keys by default, while
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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-----`),
|
||||
},
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
|
|||
return nil, err
|
||||
}
|
||||
switch {
|
||||
case privKey.Algo.Algorithm.Equal(oidRSA):
|
||||
case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA):
|
||||
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build plan9 darwin,!cgo
|
||||
// +build darwin,!cgo
|
||||
|
||||
package x509
|
||||
|
||||
|
|
|
|||
|
|
@ -98,9 +98,13 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e
|
|||
// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
|
||||
// use as a certificate chain for a SSL/TLS server.
|
||||
func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
|
||||
servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sslPara := &syscall.SSLExtraCertChainPolicyPara{
|
||||
AuthType: syscall.AUTHTYPE_SERVER,
|
||||
ServerName: syscall.StringToUTF16Ptr(opts.DNSName),
|
||||
ServerName: servernamep,
|
||||
}
|
||||
sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
|
||||
|
||||
|
|
@ -110,7 +114,7 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex
|
|||
para.Size = uint32(unsafe.Sizeof(*para))
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ const (
|
|||
// TooManyIntermediates results when a path length constraint is
|
||||
// violated.
|
||||
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
|
||||
|
|
@ -46,6 +49,8 @@ func (e CertificateInvalidError) Error() string {
|
|||
return "x509: a root or intermediate certificate is not authorized to sign in this domain"
|
||||
case TooManyIntermediates:
|
||||
return "x509: too many intermediates for path length constraint"
|
||||
case IncompatibleUsage:
|
||||
return "x509: certificate specifies an incompatible key usage"
|
||||
}
|
||||
return "x509: unknown error"
|
||||
}
|
||||
|
|
@ -84,6 +89,11 @@ type VerifyOptions struct {
|
|||
Intermediates *CertPool
|
||||
Roots *CertPool // if nil, the system roots are 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 (
|
||||
|
|
@ -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 {
|
||||
|
|
@ -300,3 +338,56 @@ func (c *Certificate) VerifyHostname(h string) error {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ type verifyTest struct {
|
|||
currentTime int64
|
||||
dnsName string
|
||||
systemSkip bool
|
||||
keyUsages []ExtKeyUsage
|
||||
|
||||
errorCallback func(*testing.T, int, error) bool
|
||||
expectedChains [][]string
|
||||
|
|
@ -113,6 +114,38 @@ var verifyTests = []verifyTest{
|
|||
{"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) {
|
||||
|
|
@ -131,6 +164,14 @@ func expectExpired(t *testing.T, i int, err error) (ok bool) {
|
|||
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) {
|
||||
if _, ok := err.(UnknownAuthorityError); !ok {
|
||||
t.Errorf("#%d: error was not UnknownAuthorityError: %s", i, err)
|
||||
|
|
@ -157,6 +198,7 @@ func testVerify(t *testing.T, useSystemRoots bool) {
|
|||
Intermediates: NewCertPool(),
|
||||
DNSName: test.dnsName,
|
||||
CurrentTime: time.Unix(test.currentTime, 0),
|
||||
KeyUsages: test.keyUsages,
|
||||
}
|
||||
|
||||
if !useSystemRoots {
|
||||
|
|
@ -433,3 +475,58 @@ O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
|
|||
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
|
||||
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
|
||||
-----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-----`
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import (
|
|||
"bytes"
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509/pkix"
|
||||
|
|
@ -106,6 +108,8 @@ type dsaSignature struct {
|
|||
R, S *big.Int
|
||||
}
|
||||
|
||||
type ecdsaSignature dsaSignature
|
||||
|
||||
type validity struct {
|
||||
NotBefore, NotAfter time.Time
|
||||
}
|
||||
|
|
@ -133,6 +137,10 @@ const (
|
|||
SHA512WithRSA
|
||||
DSAWithSHA1
|
||||
DSAWithSHA256
|
||||
ECDSAWithSHA1
|
||||
ECDSAWithSHA256
|
||||
ECDSAWithSHA384
|
||||
ECDSAWithSHA512
|
||||
)
|
||||
|
||||
type PublicKeyAlgorithm int
|
||||
|
|
@ -141,6 +149,7 @@ const (
|
|||
UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
|
||||
RSA
|
||||
DSA
|
||||
ECDSA
|
||||
)
|
||||
|
||||
// OIDs for signature algorithms
|
||||
|
|
@ -160,6 +169,12 @@ const (
|
|||
// dsaWithSha1 OBJECT IDENTIFIER ::= {
|
||||
// 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
|
||||
//
|
||||
|
|
@ -176,15 +191,30 @@ const (
|
|||
// joint-iso-ccitt(2) country(16) us(840) organization(1) gov(101)
|
||||
// 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 (
|
||||
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
|
||||
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
|
||||
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
||||
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
||||
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
|
||||
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
|
||||
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
|
||||
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2}
|
||||
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
|
||||
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
|
||||
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
||||
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
||||
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
|
||||
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
|
||||
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
|
||||
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 {
|
||||
|
|
@ -205,6 +235,14 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
|
|||
return DSAWithSHA1
|
||||
case oid.Equal(oidSignatureDSAWithSHA256):
|
||||
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
|
||||
}
|
||||
|
|
@ -218,21 +256,81 @@ func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) SignatureAlgorithm
|
|||
//
|
||||
// id-dsa OBJECT IDENTIFIER ::== { iso(1) member-body(2) us(840)
|
||||
// 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 (
|
||||
oidPublicKeyRsa = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
|
||||
oidPublicKeyDsa = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 1}
|
||||
oidPublicKeyRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 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 {
|
||||
switch {
|
||||
case oid.Equal(oidPublicKeyRsa):
|
||||
case oid.Equal(oidPublicKeyRSA):
|
||||
return RSA
|
||||
case oid.Equal(oidPublicKeyDsa):
|
||||
case oid.Equal(oidPublicKeyDSA):
|
||||
return DSA
|
||||
case oid.Equal(oidPublicKeyECDSA):
|
||||
return ECDSA
|
||||
}
|
||||
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
|
||||
// a bitmap of the KeyUsage* constants.
|
||||
type KeyUsage int
|
||||
|
|
@ -267,6 +365,9 @@ var (
|
|||
oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
|
||||
oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
|
||||
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}
|
||||
oidExtKeyUsageOCSPSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
|
||||
)
|
||||
|
|
@ -281,10 +382,48 @@ const (
|
|||
ExtKeyUsageClientAuth
|
||||
ExtKeyUsageCodeSigning
|
||||
ExtKeyUsageEmailProtection
|
||||
ExtKeyUsageIPSECEndSystem
|
||||
ExtKeyUsageIPSECTunnel
|
||||
ExtKeyUsageIPSECUser
|
||||
ExtKeyUsageTimeStamping
|
||||
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.
|
||||
type Certificate struct {
|
||||
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
|
||||
|
||||
switch algo {
|
||||
case SHA1WithRSA, DSAWithSHA1:
|
||||
case SHA1WithRSA, DSAWithSHA1, ECDSAWithSHA1:
|
||||
hashType = crypto.SHA1
|
||||
case SHA256WithRSA, DSAWithSHA256:
|
||||
case SHA256WithRSA, DSAWithSHA256, ECDSAWithSHA256:
|
||||
hashType = crypto.SHA256
|
||||
case SHA384WithRSA:
|
||||
case SHA384WithRSA, ECDSAWithSHA384:
|
||||
hashType = crypto.SHA384
|
||||
case SHA512WithRSA:
|
||||
case SHA512WithRSA, ECDSAWithSHA512:
|
||||
hashType = crypto.SHA512
|
||||
default:
|
||||
return ErrUnsupportedAlgorithm
|
||||
|
|
@ -462,6 +601,18 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
|
|||
return errors.New("DSA verification failure")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
|
@ -497,8 +648,6 @@ type nameConstraints struct {
|
|||
|
||||
type generalSubtree struct {
|
||||
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) {
|
||||
|
|
@ -540,6 +689,27 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{
|
|||
Y: p,
|
||||
}
|
||||
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:
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -694,7 +864,7 @@ func parseCertificate(in *certificate) (*Certificate, error) {
|
|||
}
|
||||
|
||||
for _, subtree := range constraints.Permitted {
|
||||
if subtree.Min > 0 || subtree.Max > 0 || len(subtree.Name) == 0 {
|
||||
if len(subtree.Name) == 0 {
|
||||
if e.Critical {
|
||||
return out, UnhandledCriticalExtension{}
|
||||
}
|
||||
|
|
@ -730,22 +900,9 @@ func parseCertificate(in *certificate) (*Certificate, error) {
|
|||
}
|
||||
|
||||
for _, u := range keyUsage {
|
||||
switch {
|
||||
case u.Equal(oidExtKeyUsageAny):
|
||||
out.ExtKeyUsage = append(out.ExtKeyUsage, ExtKeyUsageAny)
|
||||
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:
|
||||
if extKeyUsage, ok := extKeyUsageFromOID(u); ok {
|
||||
out.ExtKeyUsage = append(out.ExtKeyUsage, extKeyUsage)
|
||||
} else {
|
||||
out.UnknownExtKeyUsage = append(out.UnknownExtKeyUsage, u)
|
||||
}
|
||||
}
|
||||
|
|
@ -834,6 +991,7 @@ func reverseBitsInAByte(in byte) byte {
|
|||
var (
|
||||
oidExtensionSubjectKeyId = []int{2, 5, 29, 14}
|
||||
oidExtensionKeyUsage = []int{2, 5, 29, 15}
|
||||
oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}
|
||||
oidExtensionAuthorityKeyId = []int{2, 5, 29, 35}
|
||||
oidExtensionBasicConstraints = []int{2, 5, 29, 19}
|
||||
oidExtensionSubjectAltName = []int{2, 5, 29, 17}
|
||||
|
|
@ -842,7 +1000,7 @@ var (
|
|||
)
|
||||
|
||||
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
|
||||
|
||||
if template.KeyUsage != 0 {
|
||||
|
|
@ -865,6 +1023,27 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
|
|||
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 {
|
||||
ret[n].Id = oidExtensionBasicConstraints
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
if len(cert.RawSubject) > 0 {
|
||||
return cert.RawSubject, nil
|
||||
|
|
@ -956,8 +1130,9 @@ func subjectBytes(cert *Certificate) ([]byte, error) {
|
|||
|
||||
// CreateCertificate creates a new certificate based on a template. The
|
||||
// following members of template are used: SerialNumber, Subject, NotBefore,
|
||||
// NotAfter, KeyUsage, BasicConstraintsValid, IsCA, MaxPathLen, SubjectKeyId,
|
||||
// DNSNames, PermittedDNSDomainsCritical, PermittedDNSDomains.
|
||||
// NotAfter, KeyUsage, ExtKeyUsage, UnknownExtKeyUsage, BasicConstraintsValid,
|
||||
// IsCA, MaxPathLen, SubjectKeyId, DNSNames, PermittedDNSDomainsCritical,
|
||||
// PermittedDNSDomains.
|
||||
//
|
||||
// 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
|
||||
|
|
@ -965,23 +1140,61 @@ func subjectBytes(cert *Certificate) ([]byte, error) {
|
|||
//
|
||||
// The returned slice is the certificate in DER encoding.
|
||||
//
|
||||
// The only supported key type is RSA (*rsa.PublicKey for pub, *rsa.PrivateKey
|
||||
// for priv).
|
||||
// The only supported key types are RSA and ECDSA (*rsa.PublicKey or
|
||||
// *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) {
|
||||
rsaPub, ok := pub.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, errors.New("x509: non-RSA public keys not supported")
|
||||
var publicKeyBytes []byte
|
||||
var publicKeyAlgorithm pkix.AlgorithmIdentifier
|
||||
|
||||
switch pub := pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
publicKeyBytes, err = asn1.Marshal(rsaPublicKey{
|
||||
N: pub.N,
|
||||
E: pub.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")
|
||||
}
|
||||
|
||||
rsaPriv, ok := priv.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return nil, errors.New("x509: non-RSA private keys not 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")
|
||||
}
|
||||
|
||||
asn1PublicKey, err := asn1.Marshal(rsaPublicKey{
|
||||
N: rsaPub.N,
|
||||
E: rsaPub.E,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -1005,15 +1218,15 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
|
|||
return
|
||||
}
|
||||
|
||||
encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey}
|
||||
encodedPublicKey := asn1.BitString{BitLength: len(publicKeyBytes) * 8, Bytes: publicKeyBytes}
|
||||
c := tbsCertificate{
|
||||
Version: 2,
|
||||
SerialNumber: template.SerialNumber,
|
||||
SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
|
||||
SignatureAlgorithm: signatureAlgorithm,
|
||||
Issuer: asn1.RawValue{FullBytes: asn1Issuer},
|
||||
Validity: validity{template.NotBefore, template.NotAfter},
|
||||
Subject: asn1.RawValue{FullBytes: asn1Subject},
|
||||
PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey},
|
||||
PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},
|
||||
Extensions: extensions,
|
||||
}
|
||||
|
||||
|
|
@ -1024,11 +1237,24 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
|
|||
|
||||
c.Raw = tbsCertContents
|
||||
|
||||
h := sha1.New()
|
||||
h := hashFunc.New()
|
||||
h.Write(tbsCertContents)
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
|
@ -1036,7 +1262,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
|
|||
cert, err = asn1.Marshal(certificate{
|
||||
nil,
|
||||
c,
|
||||
pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
|
||||
signatureAlgorithm,
|
||||
asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
|
||||
})
|
||||
return
|
||||
|
|
|
|||
|
|
@ -7,14 +7,19 @@ package x509
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
_ "crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -237,65 +242,205 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
|
|||
random := rand.Reader
|
||||
|
||||
block, _ := pem.Decode([]byte(pemPrivateKey))
|
||||
priv, err := ParsePKCS1PrivateKey(block.Bytes)
|
||||
rsaPriv, err := ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to parse private key: %s", err)
|
||||
return
|
||||
t.Fatalf("Failed to parse private key: %s", err)
|
||||
}
|
||||
|
||||
commonName := "test.example.com"
|
||||
template := Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
CommonName: commonName,
|
||||
Organization: []string{"Acme Co"},
|
||||
},
|
||||
NotBefore: time.Unix(1000, 0),
|
||||
NotAfter: time.Unix(100000, 0),
|
||||
|
||||
SubjectKeyId: []byte{1, 2, 3, 4},
|
||||
KeyUsage: KeyUsageCertSign,
|
||||
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
DNSNames: []string{"test.example.com"},
|
||||
|
||||
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
|
||||
PermittedDNSDomains: []string{".example.com", "example.com"},
|
||||
}
|
||||
|
||||
derBytes, err := CreateCertificate(random, &template, &template, &priv.PublicKey, priv)
|
||||
ecdsaPriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to create certificate: %s", err)
|
||||
return
|
||||
t.Fatalf("Failed to generate ECDSA key: %s", err)
|
||||
}
|
||||
|
||||
cert, err := ParseCertificate(derBytes)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to parse certificate: %s", err)
|
||||
return
|
||||
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},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth}
|
||||
testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{3, 2, 1}}
|
||||
|
||||
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)
|
||||
}
|
||||
for _, test := range tests {
|
||||
commonName := "test.example.com"
|
||||
template := Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
CommonName: commonName,
|
||||
Organization: []string{"Σ Acme Co"},
|
||||
},
|
||||
NotBefore: time.Unix(1000, 0),
|
||||
NotAfter: time.Unix(100000, 0),
|
||||
|
||||
if cert.Subject.CommonName != commonName {
|
||||
t.Errorf("Subject wasn't correctly copied from the template. Got %s, want %s", cert.Subject.CommonName, commonName)
|
||||
}
|
||||
SubjectKeyId: []byte{1, 2, 3, 4},
|
||||
KeyUsage: KeyUsageCertSign,
|
||||
|
||||
if cert.Issuer.CommonName != commonName {
|
||||
t.Errorf("Issuer wasn't correctly copied from the template. Got %s, want %s", cert.Issuer.CommonName, commonName)
|
||||
}
|
||||
ExtKeyUsage: testExtKeyUsage,
|
||||
UnknownExtKeyUsage: testUnknownExtKeyUsage,
|
||||
|
||||
err = cert.CheckSignatureFrom(cert)
|
||||
if err != nil {
|
||||
t.Errorf("Signature verification failed: %s", err)
|
||||
return
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
DNSNames: []string{"test.example.com"},
|
||||
|
||||
PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}},
|
||||
PermittedDNSDomains: []string{".example.com", "example.com"},
|
||||
}
|
||||
|
||||
derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
|
||||
if err != nil {
|
||||
t.Errorf("%s: failed to create certificate: %s", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := ParseCertificate(derBytes)
|
||||
if err != nil {
|
||||
t.Errorf("%s: failed to parse certificate: %s", test.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(cert.PolicyIdentifiers) != 1 || !cert.PolicyIdentifiers[0].Equal(template.PolicyIdentifiers[0]) {
|
||||
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" {
|
||||
t.Errorf("%s: failed to parse name constraints: %#v", test.name, cert.PermittedDNSDomains)
|
||||
}
|
||||
|
||||
if 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 {
|
||||
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)
|
||||
if err != nil {
|
||||
t.Errorf("%s: signature verification failed: %s", test.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,19 +14,61 @@ import (
|
|||
"strconv"
|
||||
)
|
||||
|
||||
// subsetTypeArgs takes a slice of arguments from callers of the sql
|
||||
// package and converts them into a slice of the driver package's
|
||||
// "subset types".
|
||||
func subsetTypeArgs(args []interface{}) ([]driver.Value, error) {
|
||||
out := make([]driver.Value, len(args))
|
||||
// driverArgs converts arguments from callers of Stmt.Exec and
|
||||
// Stmt.Query into driver Values.
|
||||
//
|
||||
// The statement si may be nil, if no statement is available.
|
||||
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 {
|
||||
var err error
|
||||
dargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
|
||||
}
|
||||
}
|
||||
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
|
||||
out[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
|
||||
dargs[n], err = cc.ColumnConverter(n).ConvertValue(arg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sql: converting argument #%d's type: %v", n+1, err)
|
||||
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 out, nil
|
||||
|
||||
return dargs, nil
|
||||
}
|
||||
|
||||
// convertAssign copies to dest the value in src, converting it if possible.
|
||||
|
|
|
|||
|
|
@ -383,6 +383,9 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) {
|
|||
}
|
||||
|
||||
func (s *fakeStmt) ColumnConverter(idx int) driver.ValueConverter {
|
||||
if len(s.placeholderConverter) == 0 {
|
||||
return driver.DefaultParameterConverter
|
||||
}
|
||||
return s.placeholderConverter[idx]
|
||||
}
|
||||
|
||||
|
|
@ -598,6 +601,28 @@ func (rc *rowsCursor) Next(dest []driver.Value) error {
|
|||
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 {
|
||||
switch typ {
|
||||
case "bool":
|
||||
|
|
@ -607,9 +632,9 @@ func converterForType(typ string) driver.ValueConverter {
|
|||
case "int32":
|
||||
return driver.Int32
|
||||
case "string":
|
||||
return driver.NotNull{Converter: driver.String}
|
||||
return driver.NotNull{Converter: fakeDriverString{}}
|
||||
case "nullstring":
|
||||
return driver.Null{Converter: driver.String}
|
||||
return driver.Null{Converter: fakeDriverString{}}
|
||||
case "int64":
|
||||
// TODO(coopernurse): add type-specific converter
|
||||
return driver.NotNull{Converter: driver.DefaultParameterConverter}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue