├── .travis.yml ├── LICENSE.md ├── README.md ├── alg.go ├── alg_linux.go ├── alg_linux_integration_test.go ├── alg_linux_test.go ├── alg_others.go ├── alg_others_test.go ├── cmd └── algsha1sum │ └── main.go └── example_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.x 4 | os: 5 | - linux 6 | - osx 7 | before_install: 8 | - go get github.com/golang/lint/golint 9 | - go get honnef.co/go/staticcheck/cmd/staticcheck 10 | - go get -d ./... 11 | script: 12 | - go build -tags=gofuzz ./... 13 | - go vet ./... 14 | - staticcheck ./... 15 | - golint -set_exit_status ./... 16 | - go test -v -race -tags=integration ./... 17 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | =========== 3 | 4 | Copyright (C) 2017 Matt Layher 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | alg [![Build Status](https://travis-ci.org/mdlayher/alg.svg?branch=master)](https://travis-ci.org/mdlayher/alg) [![GoDoc](https://godoc.org/github.com/mdlayher/alg?status.svg)](https://godoc.org/github.com/mdlayher/alg) [![Go Report Card](https://goreportcard.com/badge/github.com/mdlayher/alg)](https://goreportcard.com/report/github.com/mdlayher/alg) 2 | === 3 | 4 | Package `alg` provides access to Linux `AF_ALG` sockets for communication 5 | with the Linux kernel crypto API. MIT Licensed. 6 | 7 | This package should be considered experimental, and should almost certainly 8 | not be used in place of Go's built-in cryptographic cipher and hash packages. 9 | 10 | The benefit of `AF_ALG` sockets is that they enable access to the Linux kernel's 11 | cryptography API, and may be able to use hardware acceleration to perform 12 | certain transformations. On systems with dedicated cryptography processing 13 | hardware (or systems without assembly implementations of certain 14 | transformations), using this package may result in a performance boost. 15 | 16 | If this package does end up being useful for you, please do reach out! 17 | I'd love to hear what you're doing with it. 18 | 19 | Benchmarking 20 | ------------ 21 | 22 | To benchmark `AF_ALG` transformations vs. the Go standard library equivalents 23 | on a given system, run the following commands: 24 | 25 | ``` 26 | $ go test -c 27 | $ ./alg.test -bench.std -test.bench . | tee std.txt 28 | $ ./alg.test -bench.alg -test.bench . | tee alg.txt 29 | $ benchcmp std.txt alg.txt 30 | ``` 31 | 32 | The `benchcmp` utility can be installed using: 33 | 34 | ``` 35 | $ go get golang.org/x/tools/cmd/benchcmp 36 | ``` 37 | -------------------------------------------------------------------------------- /alg.go: -------------------------------------------------------------------------------- 1 | // Package alg provides access to Linux AF_ALG sockets for communication 2 | // with the Linux kernel crypto API. 3 | package alg 4 | 5 | import ( 6 | "fmt" 7 | "hash" 8 | "io" 9 | ) 10 | 11 | // A Conn is a connection to the Linux kernel crypto API, using an AF_ALG 12 | // socket. A Conn can be used to initialize Hashes via its Hash method, 13 | // using the parameters configured in Dial. 14 | type Conn struct { 15 | size int 16 | blockSize int 17 | 18 | c *conn 19 | } 20 | 21 | // A Config contains optional parameters for a Conn. 22 | type Config struct { 23 | Feature uint32 24 | Mask uint32 25 | } 26 | 27 | // Dial dials a connection the Linux kernel crypto API, using the specified 28 | // transformation type, algorithm name, and optional configuration. If config 29 | // is nil, a default configuration will be used. 30 | // 31 | // At this time, the following transformation types and algorithm types are 32 | // supported: 33 | // - hash 34 | // - md5 35 | // - sha1 36 | // - sha256 37 | func Dial(typ, name string, config *Config) (*Conn, error) { 38 | if config == nil { 39 | config = &Config{} 40 | } 41 | 42 | var size, blockSize int 43 | var ok bool 44 | 45 | switch typ { 46 | case typeHash: 47 | size, blockSize, ok = hashSizes(name) 48 | if !ok { 49 | return nil, fmt.Errorf("alg: unknown hash algorithm %q", name) 50 | } 51 | default: 52 | return nil, fmt.Errorf("alg: transformation type %q unsupported", typ) 53 | } 54 | 55 | // Internal, OS-specific constructor for a conn. 56 | c, err := dial(typ, name, config) 57 | if err != nil { 58 | return nil, err 59 | } 60 | 61 | return &Conn{ 62 | size: size, 63 | blockSize: blockSize, 64 | 65 | c: c, 66 | }, nil 67 | } 68 | 69 | // Hash creates a Hash handle from a Conn. The handle is not safe for 70 | // concurrent use. 71 | func (c *Conn) Hash() (Hash, error) { 72 | return c.c.Hash(c.size, c.blockSize) 73 | } 74 | 75 | // Close closes the connection. 76 | func (c *Conn) Close() error { 77 | return c.c.Close() 78 | } 79 | 80 | // A Hash is a hash.Hash, with an added Close method. Use it just as you 81 | // would with a normal hash.Hash. The Hash's Close method must be called 82 | // to release its resources when it is no longer needed. 83 | type Hash interface { 84 | hash.Hash 85 | io.Closer 86 | } 87 | 88 | // MD5 is a convenience function for use in Dial, to open a Conn that produces 89 | // MD5 Hashes. 90 | func MD5() (string, string, *Config) { 91 | return typeHash, nameMD5, nil 92 | } 93 | 94 | // SHA1 is a convenience function for use in Dial, to open a Conn that produces 95 | // SHA1 Hashes. 96 | func SHA1() (string, string, *Config) { 97 | return typeHash, nameSHA1, nil 98 | } 99 | 100 | // SHA256 is a convenience function for use in Dial, to open a Conn that produces 101 | // SHA256 Hashes. 102 | func SHA256() (string, string, *Config) { 103 | return typeHash, nameSHA256, nil 104 | } 105 | 106 | const ( 107 | // Transformation types. 108 | typeHash = "hash" 109 | 110 | // Algorithm names. 111 | nameMD5 = "md5" 112 | nameSHA1 = "sha1" 113 | nameSHA256 = "sha256" 114 | ) 115 | 116 | // hashSizes looks up a hash by its name and returns its size and block 117 | // size, if available. If the hash is not found, false will be returned. 118 | func hashSizes(name string) (size, blockSize int, ok bool) { 119 | switch name { 120 | case nameMD5: 121 | return 16, 64, true 122 | case nameSHA1: 123 | return 20, 64, true 124 | case nameSHA256: 125 | return 32, 64, true 126 | } 127 | 128 | return 0, 0, false 129 | } 130 | -------------------------------------------------------------------------------- /alg_linux.go: -------------------------------------------------------------------------------- 1 | //+build linux 2 | 3 | package alg 4 | 5 | import ( 6 | "fmt" 7 | "syscall" 8 | 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | // A conn is the internal connection type for Linux. 13 | type conn struct { 14 | s socket 15 | addr *unix.SockaddrALG 16 | } 17 | 18 | // A socket is a wrapper around socket-related system calls, to enable 19 | // easier testing. 20 | type socket interface { 21 | Accept() (socket, error) 22 | Bind(sa unix.Sockaddr) error 23 | Close() error 24 | Read(p []byte) (int, error) 25 | Sendto(p []byte, flags int, to unix.Sockaddr) error 26 | } 27 | 28 | // dial is the entry point for Dial. dial opens an AF_ALG socket 29 | // using system calls. 30 | func dial(typ, name string, config *Config) (*conn, error) { 31 | fd, err := unix.Socket(unix.AF_ALG, unix.SOCK_SEQPACKET, 0) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | addr := &unix.SockaddrALG{ 37 | Type: typ, 38 | Name: name, 39 | Feature: config.Feature, 40 | Mask: config.Mask, 41 | } 42 | 43 | return bind(&sysSocket{fd: fd}, addr) 44 | } 45 | 46 | // bind binds an AF_ALG socket using the input socket, which may be 47 | // a system call implementation or a mocked one for tests. 48 | func bind(s socket, addr *unix.SockaddrALG) (*conn, error) { 49 | if err := s.Bind(addr); err != nil { 50 | return nil, err 51 | } 52 | 53 | return &conn{ 54 | s: s, 55 | addr: addr, 56 | }, nil 57 | } 58 | 59 | // Close closes a conn's socket. 60 | func (c *conn) Close() error { 61 | return c.s.Close() 62 | } 63 | 64 | // Hash creates a new Hash handle by accepting a single connection and 65 | // setting up an ihash. 66 | func (c *conn) Hash(size, blockSize int) (Hash, error) { 67 | s, err := c.s.Accept() 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | return &ihash{ 73 | s: s, 74 | buf: make([]byte, 128), 75 | addr: c.addr, 76 | size: size, 77 | blockSize: blockSize, 78 | }, nil 79 | } 80 | 81 | var _ Hash = &ihash{} 82 | 83 | // An ihash is the internal Linux implementation of Hash. 84 | type ihash struct { 85 | s socket 86 | buf []byte 87 | addr *unix.SockaddrALG 88 | size int 89 | blockSize int 90 | } 91 | 92 | // Close closes the ihash's socket. 93 | func (h *ihash) Close() error { 94 | return h.s.Close() 95 | } 96 | 97 | // Write writes data to an AF_ALG socket, but instructs the kernel 98 | // not to finalize the hash. 99 | func (h *ihash) Write(b []byte) (int, error) { 100 | err := h.s.Sendto(b, unix.MSG_MORE, h.addr) 101 | return len(b), err 102 | } 103 | 104 | // Sum reads data from an AF_ALG socket, and appends it to the input 105 | // buffer. 106 | func (h *ihash) Sum(b []byte) []byte { 107 | n, err := h.s.Read(h.buf) 108 | if err != nil { 109 | panic(fmt.Sprintf("alg: failed to read out finalized hash: %v", err)) 110 | } 111 | 112 | return append(b, h.buf[:n]...) 113 | } 114 | 115 | // Reset is a no-op for AF_ALG sockets. 116 | func (h *ihash) Reset() {} 117 | 118 | // BlockSize returns the block size of the hash. 119 | func (h *ihash) BlockSize() int { return h.blockSize } 120 | 121 | // Size returns the size of the hash. 122 | func (h *ihash) Size() int { return h.size } 123 | 124 | // A sysSocket is a socket which uses system calls for socket operations. 125 | type sysSocket struct { 126 | fd int 127 | } 128 | 129 | func (s *sysSocket) Accept() (socket, error) { 130 | fd, _, errno := unix.Syscall(unix.SYS_ACCEPT, uintptr(s.fd), 0, 0) 131 | if errno != 0 { 132 | return nil, syscall.Errno(errno) 133 | } 134 | 135 | // A sysSocket produces more sysSockets. 136 | return &sysSocket{ 137 | fd: int(fd), 138 | }, nil 139 | } 140 | func (s *sysSocket) Bind(sa unix.Sockaddr) error { return unix.Bind(s.fd, sa) } 141 | func (s *sysSocket) Close() error { return unix.Close(s.fd) } 142 | func (s *sysSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error { 143 | return unix.Sendto(s.fd, p, flags, to) 144 | } 145 | func (s *sysSocket) Read(p []byte) (int, error) { return unix.Read(s.fd, p) } 146 | -------------------------------------------------------------------------------- /alg_linux_integration_test.go: -------------------------------------------------------------------------------- 1 | //+build linux 2 | 3 | package alg_test 4 | 5 | import ( 6 | "bytes" 7 | "crypto/md5" 8 | "crypto/sha1" 9 | "crypto/sha256" 10 | "encoding/hex" 11 | "flag" 12 | "fmt" 13 | "hash" 14 | "io" 15 | "testing" 16 | 17 | "github.com/mdlayher/alg" 18 | ) 19 | 20 | const MB = (1 << 20) 21 | 22 | var buf = make([]byte, 512*MB) 23 | 24 | // Flags to specify using either stdlib or AF_ALG transformations. 25 | var ( 26 | flagBenchSTD = flag.Bool("bench.std", false, "benchmark only standard library transformations") 27 | flagBenchALG = flag.Bool("bench.alg", false, "benchmark only AF_ALG transformations") 28 | ) 29 | 30 | func init() { 31 | flag.Parse() 32 | } 33 | 34 | func TestMD5Equal(t *testing.T) { 35 | const expect = "0829f71740aab1ab98b33eae21dee122" 36 | withHash(t, "md5", func(algh hash.Hash) { 37 | testHashEqual(t, expect, md5.New(), algh) 38 | }) 39 | } 40 | 41 | func TestSHA1Equal(t *testing.T) { 42 | const expect = "0631457264ff7f8d5fb1edc2c0211992a67c73e6" 43 | withHash(t, "sha1", func(algh hash.Hash) { 44 | testHashEqual(t, expect, sha1.New(), algh) 45 | }) 46 | } 47 | 48 | func TestSHA256Equal(t *testing.T) { 49 | const expect = "9f1dcbc35c350d6027f98be0f5c8b43b42ca52b7604459c0c42be3aa88913d47" 50 | withHash(t, "sha256", func(algh hash.Hash) { 51 | testHashEqual(t, expect, sha256.New(), algh) 52 | }) 53 | } 54 | 55 | func BenchmarkMD5(b *testing.B) { 56 | withHash(b, "md5", func(algh hash.Hash) { 57 | benchmarkHashes(b, md5.New(), algh) 58 | }) 59 | } 60 | 61 | func BenchmarkSHA1(b *testing.B) { 62 | withHash(b, "sha1", func(algh hash.Hash) { 63 | benchmarkHashes(b, sha1.New(), algh) 64 | }) 65 | } 66 | 67 | func BenchmarkSHA256(b *testing.B) { 68 | withHash(b, "sha256", func(algh hash.Hash) { 69 | benchmarkHashes(b, sha256.New(), algh) 70 | }) 71 | } 72 | 73 | func limitReader(size int64) io.Reader { 74 | return io.LimitReader(bytes.NewBuffer(buf), size) 75 | } 76 | 77 | func testHashEqual(t *testing.T, expect string, stdh, algh hash.Hash) { 78 | const n = 8192 79 | 80 | w := io.MultiWriter(stdh, algh) 81 | r := limitReader(n) 82 | 83 | if nn, err := io.Copy(w, r); err != nil || int64(nn) != n { 84 | t.Fatalf("failed to copy: %q\n- want bytes: %d\n- got bytes: %d", 85 | err, n, nn) 86 | } 87 | 88 | cb := stdh.Sum(nil) 89 | ab := algh.Sum(nil) 90 | 91 | if want, got := cb, ab; !bytes.Equal(want, got) { 92 | t.Fatalf("unexpected hash sum:\n- std: %x\n- alg: %x", want, got) 93 | } 94 | 95 | if want, got := expect, hex.EncodeToString(ab); want != got { 96 | t.Fatalf("unexpected golden hash:\n- want: %q\n- got: %q", 97 | want, got) 98 | } 99 | } 100 | 101 | func benchmarkHashes(b *testing.B, stdh, algh hash.Hash) { 102 | sizes := []int64{ 103 | 1, 104 | 32, 105 | 64, 106 | 128, 107 | 256, 108 | 512, 109 | } 110 | 111 | for _, size := range sizes { 112 | name := fmt.Sprintf("%dMB", size) 113 | switch { 114 | case *flagBenchSTD && *flagBenchALG: 115 | b.Fatal("cannot specify both '-bench.std' and '-bench.alg'") 116 | case *flagBenchSTD: 117 | b.Run(name, func(b *testing.B) { 118 | benchmarkHash(b, size*MB, stdh) 119 | }) 120 | case *flagBenchALG: 121 | b.Run(name, func(b *testing.B) { 122 | benchmarkHash(b, size*MB, algh) 123 | }) 124 | default: 125 | b.Run(name+"/std", func(b *testing.B) { 126 | benchmarkHash(b, size*MB, stdh) 127 | }) 128 | 129 | b.Run(name+"/alg", func(b *testing.B) { 130 | benchmarkHash(b, size*MB, algh) 131 | }) 132 | } 133 | } 134 | } 135 | 136 | func benchmarkHash(b *testing.B, n int64, h hash.Hash) { 137 | b.ReportAllocs() 138 | b.ResetTimer() 139 | 140 | for i := 0; i < b.N; i++ { 141 | r := limitReader(n) 142 | 143 | if nn, err := io.Copy(h, r); err != nil || int64(nn) != n { 144 | b.Fatalf("failed to copy: %q\n- want bytes: %d\n- got bytes: %d", 145 | err, n, nn) 146 | } 147 | 148 | h.Sum(nil) 149 | h.Reset() 150 | } 151 | } 152 | 153 | func withHash(tb testing.TB, name string, fn func(h hash.Hash)) { 154 | c, err := alg.Dial("hash", name, nil) 155 | if err != nil { 156 | tb.Fatalf("failed to dial kernel: %v", err) 157 | } 158 | defer c.Close() 159 | 160 | h, err := c.Hash() 161 | if err != nil { 162 | tb.Fatalf("failed to make hash: %v", err) 163 | } 164 | defer h.Close() 165 | 166 | fn(h) 167 | } 168 | -------------------------------------------------------------------------------- /alg_linux_test.go: -------------------------------------------------------------------------------- 1 | //+build linux 2 | 3 | package alg 4 | 5 | import ( 6 | "bytes" 7 | "reflect" 8 | "testing" 9 | 10 | "golang.org/x/sys/unix" 11 | ) 12 | 13 | func TestLinuxConn_bind(t *testing.T) { 14 | addr := &unix.SockaddrALG{ 15 | Type: "hash", 16 | Name: "sha1", 17 | } 18 | 19 | s := &testSocket{} 20 | if _, err := bind(s, addr); err != nil { 21 | t.Fatalf("failed to bind: %v", err) 22 | } 23 | 24 | if want, got := addr, s.bind; !reflect.DeepEqual(want, got) { 25 | t.Fatalf("unexpected bind address:\n- want: %#v\n- got: %#v", 26 | want, got) 27 | } 28 | } 29 | 30 | func TestLinuxConnWrite(t *testing.T) { 31 | addr := &unix.SockaddrALG{ 32 | Type: "hash", 33 | Name: "sha1", 34 | } 35 | 36 | h, s := testLinuxHash(t, addr) 37 | 38 | b := []byte("hello world") 39 | if _, err := h.Write(b); err != nil { 40 | t.Fatalf("failed to write: %v", err) 41 | } 42 | 43 | if want, got := b, s.sendto.p; !bytes.Equal(want, got) { 44 | t.Fatalf("unexpected sendto bytes:\n- want: %v\n- got: %v", 45 | want, got) 46 | } 47 | 48 | if want, got := unix.MSG_MORE, s.sendto.flags; want != got { 49 | t.Fatalf("unexpected sendto flags:\n- want: %v\n- got: %v", 50 | want, got) 51 | } 52 | 53 | if want, got := addr, s.sendto.to; !reflect.DeepEqual(want, got) { 54 | t.Fatalf("unexpected sendto addr:\n- want: %v\n- got: %v", 55 | want, got) 56 | } 57 | } 58 | 59 | func TestLinuxConnSum(t *testing.T) { 60 | addr := &unix.SockaddrALG{ 61 | Type: "hash", 62 | Name: "sha1", 63 | } 64 | 65 | h, s := testLinuxHash(t, addr) 66 | s.read = []byte("deadbeef") 67 | 68 | sum := h.Sum([]byte("foo")) 69 | 70 | if want, got := []byte("foodeadbeef"), sum; !bytes.Equal(want, got) { 71 | t.Fatalf("unexpected sum bytes:\n- want: %v\n- got: %v", 72 | want, got) 73 | } 74 | } 75 | 76 | func testLinuxHash(t *testing.T, addr *unix.SockaddrALG) (Hash, *testSocket) { 77 | s := &testSocket{} 78 | c, err := bind(s, addr) 79 | if err != nil { 80 | t.Fatalf("failed to bind: %v", err) 81 | } 82 | 83 | hash, err := c.Hash(0, 0) 84 | if err != nil { 85 | t.Fatalf("failed to create hash: %v", err) 86 | } 87 | 88 | // A little gross, but gets the job done. 89 | return hash, hash.(*ihash).s.(*testSocket) 90 | } 91 | 92 | var _ socket = &testSocket{} 93 | 94 | type testSocket struct { 95 | bind unix.Sockaddr 96 | sendto struct { 97 | p []byte 98 | flags int 99 | to unix.Sockaddr 100 | } 101 | read []byte 102 | 103 | noopSocket 104 | } 105 | 106 | func (s *testSocket) Accept() (socket, error) { 107 | return &testSocket{}, nil 108 | } 109 | func (s *testSocket) Bind(sa unix.Sockaddr) error { 110 | s.bind = sa 111 | return nil 112 | } 113 | func (s *testSocket) Read(p []byte) (int, error) { 114 | n := copy(p, s.read) 115 | return n, nil 116 | } 117 | func (s *testSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error { 118 | s.sendto.p = p 119 | s.sendto.flags = flags 120 | s.sendto.to = to 121 | return nil 122 | } 123 | 124 | var _ socket = &noopSocket{} 125 | 126 | type noopSocket struct{} 127 | 128 | func (s *noopSocket) Accept() (socket, error) { return nil, nil } 129 | func (s *noopSocket) Bind(sa unix.Sockaddr) error { return nil } 130 | func (s *noopSocket) Close() error { return nil } 131 | func (s *noopSocket) Read(p []byte) (int, error) { return 0, nil } 132 | func (s *noopSocket) Sendto(p []byte, flags int, to unix.Sockaddr) error { return nil } 133 | -------------------------------------------------------------------------------- /alg_others.go: -------------------------------------------------------------------------------- 1 | //+build !linux 2 | 3 | package alg 4 | 5 | import ( 6 | "fmt" 7 | "runtime" 8 | ) 9 | 10 | var ( 11 | // errUnimplemented is returned by all functions on platforms that 12 | // cannot make use of AF_ALG sockets. 13 | errUnimplemented = fmt.Errorf("alg: AF_ALG sockets not implemented on %s/%s", 14 | runtime.GOOS, runtime.GOARCH) 15 | ) 16 | 17 | // A conn is the no-op implementation of an AF_ALG socket connection. 18 | type conn struct{} 19 | 20 | // dial is the entry point for Dial. dial always returns an error. 21 | func dial(typ, name string, config *Config) (*conn, error) { 22 | return nil, errUnimplemented 23 | } 24 | 25 | // Close always returns an error. 26 | func (c *conn) Close() error { 27 | return errUnimplemented 28 | } 29 | 30 | // Hash always returns an error. 31 | func (c *conn) Hash(size, blockSize int) (Hash, error) { 32 | return nil, errUnimplemented 33 | } 34 | -------------------------------------------------------------------------------- /alg_others_test.go: -------------------------------------------------------------------------------- 1 | //+build !linux 2 | 3 | package alg 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestOthersConnUnimplemented(t *testing.T) { 10 | c := &conn{} 11 | want := errUnimplemented 12 | 13 | if _, got := dial("", "", nil); want != got { 14 | t.Fatalf("unexpected error during dial:\n- want: %v\n- got: %v", 15 | want, got) 16 | } 17 | 18 | if _, got := c.Hash(0, 0); want != got { 19 | t.Fatalf("unexpected error during c.Hash:\n- want: %v\n- got: %v", 20 | want, got) 21 | } 22 | 23 | if got := c.Close(); want != got { 24 | t.Fatalf("unexpected error during c.Close:\n- want: %v\n- got: %v", 25 | want, got) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cmd/algsha1sum/main.go: -------------------------------------------------------------------------------- 1 | // Command algsha1sum is a Go implementation of sha1sum that uses package alg 2 | // to perform the SHA1 hashing operation. 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "os" 11 | 12 | "github.com/mdlayher/alg" 13 | ) 14 | 15 | func main() { 16 | flag.Parse() 17 | 18 | c, err := alg.Dial(alg.SHA1()) 19 | if err != nil { 20 | log.Fatalf("failed to dial kernel: %v", err) 21 | } 22 | defer c.Close() 23 | 24 | h, err := c.Hash() 25 | if err != nil { 26 | log.Fatalf("failed to create hash: %v", err) 27 | } 28 | defer h.Close() 29 | 30 | var r io.Reader 31 | arg := flag.Arg(0) 32 | switch arg { 33 | case "", "-": 34 | arg = "-" 35 | r = os.Stdin 36 | default: 37 | f, err := os.Open(arg) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | defer f.Close() 42 | 43 | r = f 44 | } 45 | 46 | if _, err := io.Copy(h, r); err != nil { 47 | log.Fatalf("failed to copy: %v", err) 48 | } 49 | 50 | fmt.Printf("%x %s\n", h.Sum(nil), arg) 51 | } 52 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package alg_test 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "io" 7 | "log" 8 | 9 | "github.com/mdlayher/alg" 10 | ) 11 | 12 | func ExampleConn_hashSHA1() { 13 | // Dial the kernel using AF_ALG sockets. The socket must be closed when it 14 | // is no longer needed. 15 | c, err := alg.Dial(alg.SHA1()) 16 | if err != nil { 17 | log.Fatalf("failed to dial kernel: %v", err) 18 | } 19 | defer c.Close() 20 | 21 | // Retrieve a hash handle from the kernel. This can be used the same as any 22 | // other hash.Hash, but must also be closed when it is no longer needed. 23 | h, err := c.Hash() 24 | if err != nil { 25 | log.Fatalf("failed to create hash: %v", err) 26 | } 27 | defer h.Close() 28 | 29 | if _, err := io.WriteString(h, "hello, world"); err != nil { 30 | log.Fatalf("failed to hash string: %v", err) 31 | } 32 | 33 | fmt.Println(hex.EncodeToString(h.Sum(nil))) 34 | } 35 | --------------------------------------------------------------------------------