├── go.mod ├── .travis.yml ├── tables_test.go ├── .github ├── changelog-configuration.json └── workflows │ └── release.yml ├── .gitignore ├── README.md ├── CONTRIBUTING.md ├── galois └── main.go ├── example_test.go ├── tables.go ├── libgfshare_test.go ├── shamir_test.go ├── shamir.go └── LICENSE /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/corvus-ch/shamir 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.8 5 | - 1.9 6 | - tip 7 | 8 | go_import_path: gopkg.in/corvus-ch/shamir.v1 9 | 10 | script: 11 | - go test -coverprofile=coverage.txt -covermode=atomic -v . 12 | 13 | after_success: 14 | - bash <(curl -s https://codecov.io/bash) 15 | -------------------------------------------------------------------------------- /tables_test.go: -------------------------------------------------------------------------------- 1 | package shamir 2 | 3 | import "testing" 4 | 5 | func TestTables(t *testing.T) { 6 | for i := 1; i < 256; i++ { 7 | logV := logTable[i] 8 | expV := expTable[logV] 9 | if expV != uint8(i) { 10 | t.Fatalf("bad: %d log: %d exp: %d", i, logV, expV) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/changelog-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "pr_template": "- ${{TITLE}} #${{NUMBER}}", 3 | "categories": [ 4 | { 5 | "title": "## 🚀 Improvements", 6 | "labels": ["enhancement"] 7 | }, 8 | { 9 | "title": "## 🐛 Fixes", 10 | "labels": ["bug"] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | coverage.txt 13 | 14 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 15 | .glide/ 16 | 17 | # Golang project vendor packages which should be ignored 18 | vendor/ 19 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: 'Release' 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | - name: Build Changelog 14 | id: log 15 | uses: mikepenz/release-changelog-builder-action@v3 16 | with: 17 | configuration: .github/changelog-configuration.json 18 | ignorePreReleases: true 19 | outputFile: .github/release-notes.md 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | - name: Create Release 24 | uses: softprops/action-gh-release@v1 25 | with: 26 | body_path: .github/release-notes.md 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shamir's Secret Sharing 2 | 3 | [![Build Status](https://img.shields.io/travis/corvus-ch/shamir.svg)](https://travis-ci.org/corvus-ch/shamir) 4 | [![Test Coverage](https://img.shields.io/codecov/c/github/corvus-ch/shamir.svg)](https://codecov.io/gh/corvus-ch/shamir) 5 | [![Documentation](https://godoc.org/gopgk.in/corvus-ch/shamir.v1?status.svg)](https://godoc.org/gopkg.in/corvus-ch/shamir.v1) 6 | 7 | Implementation of the [Shamir's Secret Sharing][sss] in golang. 8 | 9 | This package: 10 | 11 | * supports splitting and recombining of byte arrays; 12 | * supports splitting and recombining using `io.Writer` and `io.Reader` 13 | interfaces; 14 | * is compatible with `gfsplit` and `gfcombine` from [libgfshare]. 15 | 16 | Based on `github.com/hashicorp/vault` from [HashiCorp]. 17 | 18 | ## Contributing and license 19 | 20 | This library is licences under [Mozilla Public License, version 2.0](LICENSE). 21 | For information about how to contribute to this project, see 22 | [CONTRIBUTING](CONTRIBUTING.md) 23 | 24 | [sss]: https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing 25 | [libgfshare]: https://www.digital-scurf.org/software/libgfshare 26 | [HashiCorp]: https://www.hashicorp.com 27 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | I take that you have ended up here, because you have found an issue or have a 4 | question. If this is the case, please file an issue with this project on its 5 | [issue tracker over at github](https://github.com/corvus-ch/shamir/issues). 6 | 7 | Before doing so, please keep the following in mind: 8 | 9 | * The maintainer of this library is most certainly busy with a lot of other 10 | stuff. So if you struggling with using this library, please try one of the 11 | many friendly places in the internet first. You most probably will not get 12 | any support here. 13 | 14 | If you have found a bug and are willing to fix it yourself, great thanks a lot. 15 | I will happily accept your pull request given the following rules are obliged: 16 | 17 | * The bug must be reproduced by adding a test for it. 18 | * All tests must pass 19 | * Formatting rules are followed. That is, `gofmt -w .` does not result in any 20 | changes. 21 | 22 | Notes for running the tests: 23 | 24 | This library contains test to check compatibility with the binaries provided by 25 | libgfshare. Those tests are skipped if the those binaries are not present. In 26 | order to run those tests, please ensure you have [libgfshare] installed and the 27 | commands `gfsplitt` and `gfcombine` are available in your path. 28 | 29 | [libgfshare]: https://www.digital-scurf.org/software/libgfshare 30 | -------------------------------------------------------------------------------- /galois/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Helper to (re)create the galois field values in tables.go 8 | func main() { 9 | logs := make([]byte, 256) 10 | exps := make([]byte, 256) 11 | var i byte 12 | 13 | x := 1 14 | for i = 0; i < 255; i++ { 15 | exps[i] = byte(x) 16 | logs[byte(x)] = i 17 | x = x << 1 18 | if 0 != x&0x100 { 19 | // Unset the 8th bit and mix in 0x1d 20 | x = x ^ 0x11d 21 | } 22 | 23 | } 24 | // Can not log(0) so just set it neatly to 0 25 | logs[0] = 0 26 | 27 | fmt.Println(`package shamir 28 | 29 | var (`) 30 | 31 | fmt.Println("\t// logTable provides the log(X)/log(g) at each index X") 32 | fmt.Print("\tlogTable = [256]uint8{\n\t\t") 33 | for i, log := range logs { 34 | fmt.Printf("0x%02x", log) 35 | if 255 == i { 36 | fmt.Println(",") 37 | } else if 7 == i%8 { 38 | fmt.Print(",\n\t\t") 39 | } else { 40 | fmt.Print(", ") 41 | } 42 | } 43 | 44 | fmt.Print("\t}\n\n") 45 | 46 | fmt.Println("\t// expTable provides the anti-log or exponentiation value") 47 | fmt.Println("\t// for the equivalent index") 48 | fmt.Printf("\texpTable = [256]uint8{\n\t\t") 49 | for i, exp := range exps { 50 | fmt.Printf("0x%02x", exp) 51 | if 255 == i { 52 | fmt.Println(",") 53 | } else if 7 == i%8 { 54 | fmt.Print(",\n\t\t") 55 | } else { 56 | fmt.Print(", ") 57 | } 58 | } 59 | fmt.Println("\t}\n)") 60 | } 61 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package shamir 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func ExampleSplit() { 11 | secret := []byte("Hello world") 12 | parts, err := Split(secret, 3, 2) 13 | if err != nil { 14 | fmt.Fprintf(os.Stderr, "failed to split secret: %v\n", err) 15 | } 16 | fmt.Println(len(parts)) 17 | for _, part := range parts { 18 | fmt.Println(len(part)) 19 | } 20 | // Output: 21 | // 3 22 | // 11 23 | // 11 24 | // 11 25 | } 26 | 27 | func ExampleCombine() { 28 | parts := map[byte][]byte{ 29 | 71: {209, 38, 210, 117, 87, 218, 213, 140, 119, 77, 90}, 30 | 79: {27, 174, 140, 114, 4, 236, 44, 189, 215, 25, 201}, 31 | } 32 | secret, err := Combine(parts) 33 | if err != nil { 34 | fmt.Fprintf(os.Stderr, "failed to recombine secret: %v\n", err) 35 | } 36 | fmt.Println(string(secret)) 37 | // Output: 38 | // Hello world 39 | } 40 | 41 | func ExampleNewWriter() { 42 | secret := []byte("Hello world") 43 | writers := make(map[byte]*bytes.Buffer, 3) 44 | writer, err := NewWriter(3, 2, func(x byte) (io.Writer, error) { 45 | writers[x] = &bytes.Buffer{} 46 | return writers[x], nil 47 | }) 48 | if err != nil { 49 | fmt.Fprintf(os.Stderr, "failed to create secret writer: %v\n", err) 50 | } 51 | if _, err := writer.Write(secret); err != nil { 52 | fmt.Fprintf(os.Stderr, "failed to write secret: %v\n", err) 53 | } 54 | fmt.Println(len(writers)) 55 | for _, w := range writers { 56 | fmt.Println(w.Len()) 57 | } 58 | // Output 59 | // 3 60 | // 11 61 | // 11 62 | // 11 63 | } 64 | 65 | func ExampleNewReader() { 66 | readers := map[byte]io.Reader{ 67 | 71: bytes.NewBuffer([]byte{209, 38, 210, 117, 87, 218, 213, 140, 119, 77, 90}), 68 | 79: bytes.NewBuffer([]byte{27, 174, 140, 114, 4, 236, 44, 189, 215, 25, 201}), 69 | } 70 | 71 | reader, err := NewReader(readers) 72 | if err != nil { 73 | fmt.Fprintf(os.Stderr, "failed to create secret reader: %v\n", err) 74 | } 75 | secret := make([]byte, 11) 76 | if _, err := reader.Read(secret); err != nil { 77 | fmt.Fprintf(os.Stderr, "failed to read secret: %v\n", err) 78 | } 79 | fmt.Println(string(secret)) 80 | // Output: 81 | // Hello world 82 | } 83 | -------------------------------------------------------------------------------- /tables.go: -------------------------------------------------------------------------------- 1 | package shamir 2 | 3 | var ( 4 | // logTable provides the log(X)/log(g) at each index X 5 | logTable = [256]uint8{ 6 | 0x00, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 7 | 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b, 8 | 0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 9 | 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71, 10 | 0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 11 | 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45, 12 | 0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 13 | 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6, 14 | 0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 15 | 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88, 16 | 0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 17 | 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40, 18 | 0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 19 | 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d, 20 | 0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, 21 | 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57, 22 | 0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, 23 | 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18, 24 | 0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 25 | 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e, 26 | 0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 27 | 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61, 28 | 0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 29 | 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2, 30 | 0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 31 | 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6, 32 | 0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 33 | 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a, 34 | 0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 35 | 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7, 36 | 0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 37 | 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf, 38 | } 39 | 40 | // expTable provides the anti-log or exponentiation value 41 | // for the equivalent index 42 | expTable = [256]uint8{ 43 | 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 44 | 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, 45 | 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 46 | 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 47 | 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 48 | 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, 49 | 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 50 | 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, 51 | 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 52 | 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 53 | 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 54 | 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, 55 | 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 56 | 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, 57 | 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 58 | 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, 59 | 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 60 | 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, 61 | 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 62 | 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, 63 | 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 64 | 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, 65 | 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 66 | 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, 67 | 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 68 | 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, 69 | 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 70 | 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, 71 | 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 72 | 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, 73 | 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 74 | 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x00, 75 | } 76 | ) 77 | -------------------------------------------------------------------------------- /libgfshare_test.go: -------------------------------------------------------------------------------- 1 | package shamir 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | "os/exec" 10 | "path/filepath" 11 | "strconv" 12 | "testing" 13 | ) 14 | 15 | var hasGfsplit bool 16 | var hasGfcombine bool 17 | var secret = []byte{ 18 | 0xc0, 0x73, 0x62, 0x4a, 0xaf, 0x39, 0x78, 0x51, 19 | 0x4e, 0xf8, 0x44, 0x3b, 0xb2, 0xa8, 0x59, 0xc7, 20 | 0x5f, 0xc3, 0xcc, 0x6a, 0xf2, 0x6d, 0x5a, 0xaa, 21 | } 22 | 23 | func init() { 24 | hasGfsplit = hasCommand("gfsplit") 25 | hasGfcombine = hasCommand("gfcombine") 26 | } 27 | 28 | func hasCommand(name string) bool { 29 | cmd := exec.Command("command", "-v", name) 30 | cmd.Stderr = os.Stderr 31 | return nil == cmd.Run() 32 | } 33 | 34 | func TestGfsplit(t *testing.T) { 35 | if !hasGfsplit { 36 | t.Skip("Skipping because 'libgfshare' is not available.") 37 | } 38 | 39 | dir, err := ioutil.TempDir(os.TempDir(), "data") 40 | defer os.RemoveAll(dir) 41 | if err != nil { 42 | t.Fatal("failed to create temporary file") 43 | } 44 | 45 | secretFile, err := ioutil.TempFile(dir, "secret") 46 | if err != nil { 47 | t.Fatal("failed to create secret file") 48 | } 49 | if _, err := secretFile.Write(secret); err != nil { 50 | t.Fatalf("failed to write to secret file: %v", err) 51 | } 52 | secretFile.Close() 53 | 54 | split := exec.Command("gfsplit", "-n", "2", "-m", "3", secretFile.Name()) 55 | split.Dir = dir 56 | if err := split.Run(); err != nil { 57 | t.Fatalf("failed to run gfsplitt: %v", err) 58 | } 59 | 60 | partNames, err := filepath.Glob(secretFile.Name() + ".*") 61 | if err != nil { 62 | t.Errorf("failed to lookup ouput from gfsplit: %v", err) 63 | } 64 | if len(partNames) != 3 { 65 | t.Errorf("found unexpected number of files: got %d expected 3", len(partNames)) 66 | } 67 | 68 | parts := make(map[byte]io.Reader, 3) 69 | for _, name := range partNames { 70 | x, err := strconv.ParseUint(string(name[len(name)-3:]), 10, 8) 71 | if err != nil { 72 | t.Fatalf("failed to parse x coordinate of file %s: %v", name, err) 73 | } 74 | file, err := os.Open(name) 75 | if err != nil { 76 | t.Fatalf("failed to open file: %v", err) 77 | } 78 | parts[byte(x)] = file 79 | } 80 | 81 | reader, err := NewReader(parts) 82 | if err != nil { 83 | t.Fatalf("failed to create shamir reader: %v", err) 84 | } 85 | result, err := ioutil.ReadAll(reader) 86 | if err != nil { 87 | t.Fatalf("failed to combine secret: %v", err) 88 | } 89 | 90 | if !bytes.Equal(secret, result) { 91 | t.Fatalf("unexpected result:\n\texpected: %v\n\tgot: %v", secret, result) 92 | } 93 | } 94 | 95 | func TestGfcombine(t *testing.T) { 96 | if !hasGfcombine { 97 | t.Skip("Skipping because 'libgfshare' is not available.") 98 | } 99 | 100 | dir, err := ioutil.TempDir(os.TempDir(), "data") 101 | defer os.RemoveAll(dir) 102 | if err != nil { 103 | t.Fatal("failed to create temporary file") 104 | } 105 | 106 | partFiles := make(map[byte]*os.File, 3) 107 | writer, err := NewWriter(3, 2, func(x byte) (io.Writer, error) { 108 | file, err := os.Create(fmt.Sprintf("%s/part.%03d", dir, x)) 109 | partFiles[x] = file 110 | return file, err 111 | }) 112 | if _, err := writer.Write(secret); err != nil { 113 | t.Fatalf("Failed to split secret: %v", err) 114 | } 115 | for _, part := range partFiles { 116 | part.Close() 117 | } 118 | 119 | secretFileName := fmt.Sprintf("%s/secret", dir) 120 | for i, fileA := range partFiles { 121 | for j, fileB := range partFiles { 122 | if i == j { 123 | continue 124 | } 125 | combine := exec.Command("gfcombine", "-o", secretFileName, fileA.Name(), fileB.Name()) 126 | combine.Dir = dir 127 | if err := combine.Run(); err != nil { 128 | t.Fatalf("failed to combine %s and %s: %v", fileA.Name(), fileB.Name(), err) 129 | } 130 | secretFile, err := os.Open(secretFileName) 131 | if err != nil { 132 | t.Fatalf("failed to open secret file: %v", err) 133 | } 134 | result, err := ioutil.ReadAll(secretFile) 135 | if err != nil { 136 | t.Fatalf("failed to read secret file: %v", err) 137 | } 138 | if !bytes.Equal(secret, result) { 139 | t.Fatalf("unexpected result:\n\texpected: %v\n\tgot: %v", secret, result) 140 | } 141 | secretFile.Close() 142 | if err := os.Remove(secretFileName); err != nil { 143 | t.Fatalf("failed to clean up secret file: %v", err) 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /shamir_test.go: -------------------------------------------------------------------------------- 1 | package shamir 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "testing" 7 | ) 8 | 9 | func TestSplit_invalid(t *testing.T) { 10 | secret := []byte("test") 11 | 12 | if _, err := Split(secret, 0, 0); err == nil { 13 | t.Fatalf("expect error") 14 | } 15 | 16 | if _, err := Split(secret, 2, 3); err == nil { 17 | t.Fatalf("expect error") 18 | } 19 | 20 | if _, err := Split(secret, 1000, 3); err == nil { 21 | t.Fatalf("expect error") 22 | } 23 | 24 | if _, err := Split(secret, 10, 1); err == nil { 25 | t.Fatalf("expect error") 26 | } 27 | } 28 | 29 | func TestSplit_noZeroCoordinate(t *testing.T) { 30 | secret := []byte("test") 31 | 32 | for i := 0; i < 1000; i++ { 33 | out, err := Split(secret, 5, 3) 34 | if err != nil { 35 | t.Fatalf("err: %v", err) 36 | } 37 | 38 | for x, _ := range out { 39 | if x == 0 { 40 | t.Fatal("found zero coordinate, the values therefore reveal the secret") 41 | } 42 | } 43 | } 44 | } 45 | 46 | func TestSplit(t *testing.T) { 47 | secret := []byte("test") 48 | 49 | out, err := Split(secret, 5, 3) 50 | if err != nil { 51 | t.Fatalf("err: %v", err) 52 | } 53 | 54 | if len(out) != 5 { 55 | t.Fatalf("bad: %v", out) 56 | } 57 | 58 | for _, share := range out { 59 | if len(share) != len(secret) { 60 | t.Fatalf("bad: %v", out) 61 | } 62 | } 63 | } 64 | 65 | func TestSplit_large(t *testing.T) { 66 | secret := []byte("test") 67 | 68 | out, err := Split(secret, 255, 255) 69 | if err != nil { 70 | t.Fatalf("err: %v", err) 71 | } 72 | 73 | if len(out) != 255 { 74 | t.Fatalf("wrong number of shards: %v", out) 75 | } 76 | 77 | for _, share := range out { 78 | if len(share) != len(secret) { 79 | t.Fatalf("bad: %v", out) 80 | } 81 | } 82 | } 83 | 84 | func TestCombine_invalid(t *testing.T) { 85 | // Not enough parts 86 | if _, err := Combine(nil); err == nil { 87 | t.Fatalf("should err") 88 | } 89 | 90 | // Mis-match in length 91 | parts := map[byte][]byte{ 92 | 42: []byte("foo"), 93 | 24: []byte("ba"), 94 | } 95 | if _, err := Combine(parts); err == nil { 96 | t.Fatalf("should err") 97 | } 98 | 99 | //Too short 100 | parts = map[byte][]byte{ 101 | 1: []byte(""), 102 | 2: []byte(""), 103 | } 104 | if _, err := Combine(parts); err == nil { 105 | t.Fatalf("should err") 106 | } 107 | } 108 | 109 | func TestCombine(t *testing.T) { 110 | secret := []byte("test") 111 | 112 | out, err := Split(secret, 5, 3) 113 | if err != nil { 114 | t.Fatalf("err: %v", err) 115 | } 116 | 117 | keys := make([]byte, len(out)) 118 | 119 | i := 0 120 | for k := range out { 121 | keys[i] = k 122 | i++ 123 | } 124 | 125 | // There is 5*4*3 possible choices, 126 | // we will just brute force try them all 127 | for i := uint8(0); i < 5; i++ { 128 | for j := uint8(0); j < 5; j++ { 129 | if j == i { 130 | continue 131 | } 132 | for k := uint8(0); k < 5; k++ { 133 | if k == i || k == j { 134 | continue 135 | } 136 | parts := map[byte][]byte{ 137 | keys[i]: out[keys[i]], 138 | keys[j]: out[keys[j]], 139 | keys[k]: out[keys[k]], 140 | } 141 | recomb, err := Combine(parts) 142 | if err != nil { 143 | t.Fatalf("err: %v", err) 144 | } 145 | 146 | if !bytes.Equal(recomb, secret) { 147 | t.Errorf("parts: (i:%d, j:%d, k:%d) %v", i, j, k, parts) 148 | t.Fatalf("bad: %v %v", recomb, secret) 149 | } 150 | 151 | readers := map[byte]io.Reader{ 152 | keys[i]: bytes.NewReader(out[keys[i]]), 153 | keys[j]: bytes.NewReader(out[keys[j]]), 154 | keys[k]: bytes.NewReader(out[keys[k]]), 155 | } 156 | 157 | r, err := NewReader(readers) 158 | if nil != err { 159 | t.Errorf("failed to create reader: %v", err) 160 | } 161 | recomb2 := make([]byte, len(secret)) 162 | if _, err := r.Read(recomb2); nil != err { 163 | t.Errorf("failed to combine secret: %v", err) 164 | } 165 | if !bytes.Equal(recomb2, secret) { 166 | t.Errorf("parts: (i:%d, j:%d, k:%d) %v", i, j, k, parts) 167 | t.Fatalf("bad: %v %v", recomb2, secret) 168 | } 169 | } 170 | } 171 | } 172 | } 173 | 174 | func TestField_Add(t *testing.T) { 175 | if out := add(16, 16); out != 0 { 176 | t.Fatalf("Bad: %v 16", out) 177 | } 178 | 179 | if out := add(3, 4); out != 7 { 180 | t.Fatalf("Bad: %v 7", out) 181 | } 182 | } 183 | 184 | func TestField_Mult(t *testing.T) { 185 | if out := mult(3, 7); out != 9 { 186 | t.Fatalf("Bad: %v 9", out) 187 | } 188 | 189 | if out := mult(3, 0); out != 0 { 190 | t.Fatalf("Bad: %v 0", out) 191 | } 192 | 193 | if out := mult(0, 3); out != 0 { 194 | t.Fatalf("Bad: %v 0", out) 195 | } 196 | } 197 | 198 | func TestField_Divide(t *testing.T) { 199 | if out := div(0, 7); out != 0 { 200 | t.Fatalf("Bad: %v 0", out) 201 | } 202 | 203 | if out := div(3, 3); out != 1 { 204 | t.Fatalf("Bad: %v 1", out) 205 | } 206 | 207 | if out := div(6, 3); out != 2 { 208 | t.Fatalf("Bad: %v 2", out) 209 | } 210 | } 211 | 212 | func TestPolynomial_Random(t *testing.T) { 213 | p, err := makePolynomial(42, 2) 214 | if err != nil { 215 | t.Fatalf("err: %v", err) 216 | } 217 | 218 | if p.coefficients[0] != 42 { 219 | t.Fatalf("bad: %v", p.coefficients) 220 | } 221 | } 222 | 223 | func TestPolynomial_Eval(t *testing.T) { 224 | p, err := makePolynomial(42, 1) 225 | if err != nil { 226 | t.Fatalf("err: %v", err) 227 | } 228 | 229 | if out := p.evaluate(0); out != 42 { 230 | t.Fatalf("bad: %v", out) 231 | } 232 | 233 | out := p.evaluate(1) 234 | exp := add(42, mult(1, p.coefficients[1])) 235 | if out != exp { 236 | t.Fatalf("bad: %v %v %v", out, exp, p.coefficients) 237 | } 238 | } 239 | 240 | func TestInterpolate_Rand(t *testing.T) { 241 | for i := 0; i < 256; i++ { 242 | p, err := makePolynomial(uint8(i), 2) 243 | if err != nil { 244 | t.Fatalf("err: %v", err) 245 | } 246 | 247 | pairs := []pair{ 248 | {x: 1, y: p.evaluate(1)}, 249 | {x: 2, y: p.evaluate(2)}, 250 | {x: 3, y: p.evaluate(3)}, 251 | } 252 | 253 | out := interpolate(pairs, 0) 254 | if out != uint8(i) { 255 | t.Fatalf("Bad: %v %d", out, i) 256 | } 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /shamir.go: -------------------------------------------------------------------------------- 1 | package shamir 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "crypto/subtle" 7 | "fmt" 8 | "io" 9 | ) 10 | 11 | // an x/y pair 12 | type pair struct { 13 | x, y byte 14 | } 15 | 16 | // polynomial represents a polynomial of arbitrary degree 17 | type polynomial struct { 18 | coefficients []uint8 19 | } 20 | 21 | // makePolynomial constructs a random polynomial of the given 22 | // degree but with the provided intercept value. 23 | func makePolynomial(intercept, degree uint8) (polynomial, error) { 24 | // Create a wrapper 25 | p := polynomial{ 26 | coefficients: make([]byte, degree+1), 27 | } 28 | 29 | // Ensure the intercept is set 30 | p.coefficients[0] = intercept 31 | 32 | // Assign random co-efficients to the polynomial 33 | if _, err := rand.Read(p.coefficients[1:]); err != nil { 34 | return p, err 35 | } 36 | 37 | return p, nil 38 | } 39 | 40 | // evaluate returns the value of the polynomial for the given x 41 | func (p *polynomial) evaluate(x byte) byte { 42 | // Special case the origin 43 | if x == 0 { 44 | return p.coefficients[0] 45 | } 46 | 47 | // Compute the polynomial value using Horner's method. 48 | degree := len(p.coefficients) - 1 49 | out := p.coefficients[degree] 50 | for i := degree - 1; i >= 0; i-- { 51 | coeff := p.coefficients[i] 52 | out = add(mult(out, x), coeff) 53 | } 54 | return out 55 | } 56 | 57 | // Lagrange interpolation 58 | // 59 | // Takes N sample points and returns the value at a given x using a lagrange interpolation. 60 | func interpolate(points []pair, x byte) (value byte) { 61 | for i, a := range points { 62 | weight := byte(1) 63 | for j, b := range points { 64 | if i != j { 65 | top := x ^ b.x 66 | bottom := a.x ^ b.x 67 | factor := div(top, bottom) 68 | weight = mult(weight, factor) 69 | } 70 | } 71 | value = value ^ mult(weight, a.y) 72 | } 73 | return 74 | } 75 | 76 | // div divides two numbers in GF(2^8) 77 | func div(a, b uint8) uint8 { 78 | if b == 0 { 79 | // leaks some timing information but we don't care anyways as this 80 | // should never happen, hence the panic 81 | panic("divide by zero") 82 | } 83 | 84 | var goodVal, zero uint8 85 | log_a := logTable[a] 86 | log_b := logTable[b] 87 | diff := (int(log_a) - int(log_b)) % 255 88 | if diff < 0 { 89 | diff += 255 90 | } 91 | 92 | ret := expTable[diff] 93 | 94 | // Ensure we return zero if a is zero but aren't subject to timing attacks 95 | goodVal = ret 96 | 97 | if subtle.ConstantTimeByteEq(a, 0) == 1 { 98 | ret = zero 99 | } else { 100 | ret = goodVal 101 | } 102 | 103 | return ret 104 | } 105 | 106 | // mult multiplies two numbers in GF(2^8) 107 | func mult(a, b uint8) (out uint8) { 108 | var goodVal, zero uint8 109 | log_a := logTable[a] 110 | log_b := logTable[b] 111 | sum := (int(log_a) + int(log_b)) % 255 112 | 113 | ret := expTable[sum] 114 | 115 | // Ensure we return zero if either a or be are zero but aren't subject to 116 | // timing attacks 117 | goodVal = ret 118 | 119 | if subtle.ConstantTimeByteEq(a, 0) == 1 { 120 | ret = zero 121 | } else { 122 | ret = goodVal 123 | } 124 | 125 | if subtle.ConstantTimeByteEq(b, 0) == 1 { 126 | ret = zero 127 | } else { 128 | // This operation does not do anything logically useful. It 129 | // only ensures a constant number of assignments to thwart 130 | // timing attacks. 131 | goodVal = zero 132 | } 133 | 134 | return ret 135 | } 136 | 137 | // add combines two numbers in GF(2^8) 138 | // This can also be used for subtraction since it is symmetric. 139 | func add(a, b uint8) uint8 { 140 | return a ^ b 141 | } 142 | 143 | type writer struct { 144 | io.Writer 145 | writers map[byte]io.Writer 146 | threshold int 147 | bytesWritten int 148 | } 149 | 150 | func (w *writer) Write(p []byte) (int, error) { 151 | n := 0 152 | // Construct a random polynomial for each byte of the secret. 153 | // Because we are using a field of size 256, we can only represent 154 | // a single byte as the intercept of the polynomial, so we must 155 | // use a new polynomial for each byte. 156 | for _, val := range p { 157 | p, err := makePolynomial(val, uint8(w.threshold-1)) 158 | if nil != err { 159 | return n, fmt.Errorf("failed to generate polynomial: %v", err) 160 | } 161 | 162 | // Generate a `parts` number of (x,y) pairs 163 | // We cheat by encoding the x value once as the final index, 164 | // so that it only needs to be stored once. 165 | for x, w := range w.writers { 166 | y := p.evaluate(uint8(x)) 167 | _, err := w.Write([]byte{y}) 168 | if nil != err { 169 | return n, fmt.Errorf("failed to write part: %v", err) 170 | } 171 | } 172 | n++ 173 | w.bytesWritten += n 174 | } 175 | 176 | return n, nil 177 | } 178 | 179 | func NewWriter(parts, threshold int, factory func(x byte) (io.Writer, error)) (io.Writer, error) { 180 | // Sanity check the input 181 | if parts < threshold { 182 | return nil, fmt.Errorf("parts cannot be less than threshold") 183 | } 184 | if parts > 255 { 185 | return nil, fmt.Errorf("parts cannot exceed 255") 186 | } 187 | if threshold < 2 { 188 | return nil, fmt.Errorf("threshold must be at least 2") 189 | } 190 | 191 | result := writer{writers: make(map[byte]io.Writer, parts), threshold: threshold} 192 | 193 | buf := make([]byte, 1) 194 | for len(result.writers) < parts { 195 | if _, err := rand.Read(buf); err != nil { 196 | return nil, err 197 | } 198 | x := buf[0] 199 | 200 | if x == 0 { 201 | // We cannot use a zero x coordinate otherwise the y values would be the intercepts i.e. the secret value itself. 202 | continue 203 | } 204 | if _, exists := result.writers[x]; exists { 205 | continue 206 | } 207 | 208 | w, err := factory(x) 209 | if nil != err { 210 | return nil, err 211 | } 212 | result.writers[x] = w 213 | } 214 | 215 | return &result, nil 216 | } 217 | 218 | // Split takes an arbitrarily long secret and generates a `parts` 219 | // number of shares, `threshold` of which are required to reconstruct 220 | // the secret. The parts and threshold must be at least 2, and less 221 | // than 256. The returned shares are each one byte longer than the secret 222 | // as they attach a tag used to reconstruct the secret. 223 | func Split(secret []byte, parts, threshold int) (map[byte][]byte, error) { 224 | buffers := make(map[byte]*bytes.Buffer, parts) 225 | factory := func(x byte) (io.Writer, error) { 226 | buffers[x] = &bytes.Buffer{} 227 | return buffers[x], nil 228 | } 229 | s, err := NewWriter(parts, threshold, factory) 230 | if nil != err { 231 | return nil, fmt.Errorf("failed to initilize writer: %v", err) 232 | } 233 | 234 | if _, err := s.Write(secret); nil != err { 235 | return nil, fmt.Errorf("failed to split secret: %v", err) 236 | } 237 | 238 | out := make(map[byte][]byte, parts) 239 | for x, buf := range buffers { 240 | out[x] = buf.Bytes() 241 | } 242 | 243 | // Return the encoded secrets 244 | return out, nil 245 | } 246 | 247 | // Combine is used to reverse a Split and reconstruct a secret 248 | // once a `threshold` number of parts are available. 249 | func Combine(parts map[byte][]byte) ([]byte, error) { 250 | // Verify enough parts provided 251 | if len(parts) < 2 { 252 | return nil, fmt.Errorf("less than two parts cannot be used to reconstruct the secret") 253 | } 254 | 255 | // Verify the parts are all the same length 256 | var firstPartLen int 257 | for x := range parts { 258 | firstPartLen = len(parts[x]) 259 | break 260 | } 261 | if firstPartLen < 1 { 262 | return nil, fmt.Errorf("parts must be at least one byte long") 263 | } 264 | for _, part := range parts { 265 | if len(part) != firstPartLen { 266 | return nil, fmt.Errorf("all parts must be the same length") 267 | } 268 | } 269 | 270 | // Create a buffer to store the reconstructed secret 271 | secret := make([]byte, firstPartLen) 272 | points := make([]pair, len(parts)) 273 | 274 | for i := range secret { 275 | p := 0 276 | for k, v := range parts { 277 | points[p] = pair{x: k, y: v[i]} 278 | p++ 279 | } 280 | secret[i] = interpolate(points, 0) 281 | } 282 | 283 | return secret, nil 284 | } 285 | 286 | type reader struct { 287 | io.Reader 288 | readers map[byte]io.Reader 289 | eof bool 290 | } 291 | 292 | func NewReader(readers map[byte]io.Reader) (io.Reader, error) { 293 | // Verify enough parts provided 294 | if len(readers) < 2 { 295 | return nil, fmt.Errorf("at least two parts are required to reconstruct the secret") 296 | } 297 | return &reader{readers: readers}, nil 298 | } 299 | 300 | func (r *reader) Read(p []byte) (int, error) { 301 | if r.eof { 302 | return 0, io.EOF 303 | } 304 | 305 | points := make([][]pair, len(p)) 306 | for i := range points { 307 | points[i] = make([]pair, len(r.readers)) 308 | } 309 | 310 | j := 0 311 | n := 0 312 | 313 | for x, ir := range r.readers { 314 | buf := make([]byte, len(p)) 315 | m, err := ir.Read(buf) 316 | if io.EOF == err { 317 | r.eof = true 318 | } else if nil != err { 319 | return 0, err 320 | } else if 0 != n && 0 != m && m != n { 321 | return 0, fmt.Errorf("input must be of equal length") 322 | } 323 | n = m 324 | 325 | for i := 0; i < m; i++ { 326 | points[i][j] = pair{x: x, y: buf[i]} 327 | } 328 | j++ 329 | } 330 | 331 | for m := 0; m < n; m++ { 332 | p[m] = interpolate(points[m], 0) 333 | } 334 | 335 | return n, nil 336 | } 337 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. "Contributor" 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. "Contributor Version" 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. "Covered Software" 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the terms of 34 | a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. "Larger Work" 41 | 42 | means a work that combines Covered Software with other material, in a 43 | separate file or files, that is not Covered Software. 44 | 45 | 1.8. "License" 46 | 47 | means this document. 48 | 49 | 1.9. "Licensable" 50 | 51 | means having the right to grant, to the maximum extent possible, whether 52 | at the time of the initial grant or subsequently, any and all of the 53 | rights conveyed by this License. 54 | 55 | 1.10. "Modifications" 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, 60 | deletion from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. "Patent Claims" of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, 67 | process, and apparatus claims, in any patent Licensable by such 68 | Contributor that would be infringed, but for the grant of the License, 69 | by the making, using, selling, offering for sale, having made, import, 70 | or transfer of either its Contributions or its Contributor Version. 71 | 72 | 1.12. "Secondary License" 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. "Source Code Form" 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. "You" (or "Your") 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, "You" includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, "control" means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or 104 | as part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its 108 | Contributions or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution 113 | become effective for each Contribution on the date the Contributor first 114 | distributes such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under 119 | this License. No additional rights or licenses will be implied from the 120 | distribution or licensing of Covered Software under this License. 121 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 122 | Contributor: 123 | 124 | a. for any code that a Contributor has removed from Covered Software; or 125 | 126 | b. for infringements caused by: (i) Your and any other third party's 127 | modifications of Covered Software, or (ii) the combination of its 128 | Contributions with other software (except as part of its Contributor 129 | Version); or 130 | 131 | c. under Patent Claims infringed by Covered Software in the absence of 132 | its Contributions. 133 | 134 | This License does not grant any rights in the trademarks, service marks, 135 | or logos of any Contributor (except as may be necessary to comply with 136 | the notice requirements in Section 3.4). 137 | 138 | 2.4. Subsequent Licenses 139 | 140 | No Contributor makes additional grants as a result of Your choice to 141 | distribute the Covered Software under a subsequent version of this 142 | License (see Section 10.2) or under the terms of a Secondary License (if 143 | permitted under the terms of Section 3.3). 144 | 145 | 2.5. Representation 146 | 147 | Each Contributor represents that the Contributor believes its 148 | Contributions are its original creation(s) or it has sufficient rights to 149 | grant the rights to its Contributions conveyed by this License. 150 | 151 | 2.6. Fair Use 152 | 153 | This License is not intended to limit any rights You have under 154 | applicable copyright doctrines of fair use, fair dealing, or other 155 | equivalents. 156 | 157 | 2.7. Conditions 158 | 159 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 160 | Section 2.1. 161 | 162 | 163 | 3. Responsibilities 164 | 165 | 3.1. Distribution of Source Form 166 | 167 | All distribution of Covered Software in Source Code Form, including any 168 | Modifications that You create or to which You contribute, must be under 169 | the terms of this License. You must inform recipients that the Source 170 | Code Form of the Covered Software is governed by the terms of this 171 | License, and how they can obtain a copy of this License. You may not 172 | attempt to alter or restrict the recipients' rights in the Source Code 173 | Form. 174 | 175 | 3.2. Distribution of Executable Form 176 | 177 | If You distribute Covered Software in Executable Form then: 178 | 179 | a. such Covered Software must also be made available in Source Code Form, 180 | as described in Section 3.1, and You must inform recipients of the 181 | Executable Form how they can obtain a copy of such Source Code Form by 182 | reasonable means in a timely manner, at a charge no more than the cost 183 | of distribution to the recipient; and 184 | 185 | b. You may distribute such Executable Form under the terms of this 186 | License, or sublicense it under different terms, provided that the 187 | license for the Executable Form does not attempt to limit or alter the 188 | recipients' rights in the Source Code Form under this License. 189 | 190 | 3.3. Distribution of a Larger Work 191 | 192 | You may create and distribute a Larger Work under terms of Your choice, 193 | provided that You also comply with the requirements of this License for 194 | the Covered Software. If the Larger Work is a combination of Covered 195 | Software with a work governed by one or more Secondary Licenses, and the 196 | Covered Software is not Incompatible With Secondary Licenses, this 197 | License permits You to additionally distribute such Covered Software 198 | under the terms of such Secondary License(s), so that the recipient of 199 | the Larger Work may, at their option, further distribute the Covered 200 | Software under the terms of either this License or such Secondary 201 | License(s). 202 | 203 | 3.4. Notices 204 | 205 | You may not remove or alter the substance of any license notices 206 | (including copyright notices, patent notices, disclaimers of warranty, or 207 | limitations of liability) contained within the Source Code Form of the 208 | Covered Software, except that You may alter any license notices to the 209 | extent required to remedy known factual inaccuracies. 210 | 211 | 3.5. Application of Additional Terms 212 | 213 | You may choose to offer, and to charge a fee for, warranty, support, 214 | indemnity or liability obligations to one or more recipients of Covered 215 | Software. However, You may do so only on Your own behalf, and not on 216 | behalf of any Contributor. You must make it absolutely clear that any 217 | such warranty, support, indemnity, or liability obligation is offered by 218 | You alone, and You hereby agree to indemnify every Contributor for any 219 | liability incurred by such Contributor as a result of warranty, support, 220 | indemnity or liability terms You offer. You may include additional 221 | disclaimers of warranty and limitations of liability specific to any 222 | jurisdiction. 223 | 224 | 4. Inability to Comply Due to Statute or Regulation 225 | 226 | If it is impossible for You to comply with any of the terms of this License 227 | with respect to some or all of the Covered Software due to statute, 228 | judicial order, or regulation then You must: (a) comply with the terms of 229 | this License to the maximum extent possible; and (b) describe the 230 | limitations and the code they affect. Such description must be placed in a 231 | text file included with all distributions of the Covered Software under 232 | this License. Except to the extent prohibited by statute or regulation, 233 | such description must be sufficiently detailed for a recipient of ordinary 234 | skill to be able to understand it. 235 | 236 | 5. Termination 237 | 238 | 5.1. The rights granted under this License will terminate automatically if You 239 | fail to comply with any of its terms. However, if You become compliant, 240 | then the rights granted under this License from a particular Contributor 241 | are reinstated (a) provisionally, unless and until such Contributor 242 | explicitly and finally terminates Your grants, and (b) on an ongoing 243 | basis, if such Contributor fails to notify You of the non-compliance by 244 | some reasonable means prior to 60 days after You have come back into 245 | compliance. Moreover, Your grants from a particular Contributor are 246 | reinstated on an ongoing basis if such Contributor notifies You of the 247 | non-compliance by some reasonable means, this is the first time You have 248 | received notice of non-compliance with this License from such 249 | Contributor, and You become compliant prior to 30 days after Your receipt 250 | of the notice. 251 | 252 | 5.2. If You initiate litigation against any entity by asserting a patent 253 | infringement claim (excluding declaratory judgment actions, 254 | counter-claims, and cross-claims) alleging that a Contributor Version 255 | directly or indirectly infringes any patent, then the rights granted to 256 | You by any and all Contributors for the Covered Software under Section 257 | 2.1 of this License shall terminate. 258 | 259 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 260 | license agreements (excluding distributors and resellers) which have been 261 | validly granted by You or Your distributors under this License prior to 262 | termination shall survive termination. 263 | 264 | 6. Disclaimer of Warranty 265 | 266 | Covered Software is provided under this License on an "as is" basis, 267 | without warranty of any kind, either expressed, implied, or statutory, 268 | including, without limitation, warranties that the Covered Software is free 269 | of defects, merchantable, fit for a particular purpose or non-infringing. 270 | The entire risk as to the quality and performance of the Covered Software 271 | is with You. Should any Covered Software prove defective in any respect, 272 | You (not any Contributor) assume the cost of any necessary servicing, 273 | repair, or correction. This disclaimer of warranty constitutes an essential 274 | part of this License. No use of any Covered Software is authorized under 275 | this License except under this disclaimer. 276 | 277 | 7. Limitation of Liability 278 | 279 | Under no circumstances and under no legal theory, whether tort (including 280 | negligence), contract, or otherwise, shall any Contributor, or anyone who 281 | distributes Covered Software as permitted above, be liable to You for any 282 | direct, indirect, special, incidental, or consequential damages of any 283 | character including, without limitation, damages for lost profits, loss of 284 | goodwill, work stoppage, computer failure or malfunction, or any and all 285 | other commercial damages or losses, even if such party shall have been 286 | informed of the possibility of such damages. This limitation of liability 287 | shall not apply to liability for death or personal injury resulting from 288 | such party's negligence to the extent applicable law prohibits such 289 | limitation. Some jurisdictions do not allow the exclusion or limitation of 290 | incidental or consequential damages, so this exclusion and limitation may 291 | not apply to You. 292 | 293 | 8. Litigation 294 | 295 | Any litigation relating to this License may be brought only in the courts 296 | of a jurisdiction where the defendant maintains its principal place of 297 | business and such litigation shall be governed by laws of that 298 | jurisdiction, without reference to its conflict-of-law provisions. Nothing 299 | in this Section shall prevent a party's ability to bring cross-claims or 300 | counter-claims. 301 | 302 | 9. Miscellaneous 303 | 304 | This License represents the complete agreement concerning the subject 305 | matter hereof. If any provision of this License is held to be 306 | unenforceable, such provision shall be reformed only to the extent 307 | necessary to make it enforceable. Any law or regulation which provides that 308 | the language of a contract shall be construed against the drafter shall not 309 | be used to construe this License against a Contributor. 310 | 311 | 312 | 10. Versions of the License 313 | 314 | 10.1. New Versions 315 | 316 | Mozilla Foundation is the license steward. Except as provided in Section 317 | 10.3, no one other than the license steward has the right to modify or 318 | publish new versions of this License. Each version will be given a 319 | distinguishing version number. 320 | 321 | 10.2. Effect of New Versions 322 | 323 | You may distribute the Covered Software under the terms of the version 324 | of the License under which You originally received the Covered Software, 325 | or under the terms of any subsequent version published by the license 326 | steward. 327 | 328 | 10.3. Modified Versions 329 | 330 | If you create software not governed by this License, and you want to 331 | create a new license for such software, you may create and use a 332 | modified version of this License if you rename the license and remove 333 | any references to the name of the license steward (except to note that 334 | such modified license differs from this License). 335 | 336 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 337 | Licenses If You choose to distribute Source Code Form that is 338 | Incompatible With Secondary Licenses under the terms of this version of 339 | the License, the notice described in Exhibit B of this License must be 340 | attached. 341 | 342 | Exhibit A - Source Code Form License Notice 343 | 344 | This Source Code Form is subject to the 345 | terms of the Mozilla Public License, v. 346 | 2.0. If a copy of the MPL was not 347 | distributed with this file, You can 348 | obtain one at 349 | http://mozilla.org/MPL/2.0/. 350 | 351 | If it is not possible or desirable to put the notice in a particular file, 352 | then You may include the notice in a location (such as a LICENSE file in a 353 | relevant directory) where a recipient would be likely to look for such a 354 | notice. 355 | 356 | You may add additional accurate notices of copyright ownership. 357 | 358 | Exhibit B - "Incompatible With Secondary Licenses" Notice 359 | 360 | This Source Code Form is "Incompatible 361 | With Secondary Licenses", as defined by 362 | the Mozilla Public License, v. 2.0. 363 | 364 | --------------------------------------------------------------------------------