├── .gitignore ├── .gitmodules ├── LICENSE ├── Makefile ├── README.md ├── core.go ├── go.mod ├── hash.go ├── hydrogen.go ├── hydrogen_test.go ├── kdf.go ├── kx.go ├── pwhash.go ├── random.go ├── secretbox.go ├── sign.go ├── tests └── main.go ├── version.go └── version_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Project 15 | .local 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libhydrogen"] 2 | path = libhydrogen 3 | url = https://github.com/jedisct1/libhydrogen.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * ISC License 3 | * 4 | * Copyright (c) 2018 5 | * Jeff Hufford 6 | * 7 | * Permission to use, copy, modify, and/or distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # libhydrogen-go Makefile 3 | # 4 | 5 | SHELL :=/bin/bash 6 | 7 | ################################################################################ 8 | .PHONY: libhydrogen libhydrogen-clean 9 | all: 10 | @echo "Help:" 11 | @echo "make libhydrogen | build libhydrogen" 12 | @echo "make libhydrogen-clean | clean libhydrogen" 13 | @echo "make run | run examples" 14 | @echo "make run-locallib | run examples" 15 | 16 | libhydrogen: 17 | make -C libhydrogen 18 | 19 | libhydrogen-clean: 20 | make -C libhydrogen clean 21 | 22 | ################################################################################ 23 | .PHONY: fmt run test run_custom_ld rerun 24 | fmt: 25 | go fmt ./...; 26 | 27 | test: 28 | env CGO_ENABLED=1 go test *_test.go 29 | 30 | run: 31 | env CGO_ENABLED=1 go run tests/main.go 32 | 33 | run-locallib: 34 | env CGO_ENABLED=1 CGO_LDFLAGS="-Llibhydrogen" CGO_CFLAGS="-Ilibhydrogen" go run tests/main.go 35 | 36 | run_custom_ld: 37 | env CGO_ENABLED=1 CGO_LDFLAGS="-L/path/to/libhydrogen" CGO_CFLAGS="-I/path/to/libhydrogen" go run tests/main.go 38 | 39 | rerun: 40 | env CGO_ENABLED=1 go run -a tests/main.go 41 | ################################################################################ 42 | .PHONY: race 43 | race: 44 | env CGO_ENABLED=1 go run -race tests/main.go 45 | 46 | # 47 | # End 48 | # 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libhydrogen-go 2 | 3 | Golang wrapper for [libhydrogen](https://github.com/jedisct1/libhydrogen). 4 | 5 | ## Usage 6 | 7 | If libhydrogen is already installed system-wide, just `go get` the project 8 | and import `libhydrogen-go` like anything else. Otherwise, you may build and 9 | install `libhydrogen.a` from the submodule directory. 10 | 11 | **Note**: `libhydrogen-go` is only tested to work against the version of 12 | libhydrogen included as a submodule in this repo. 13 | 14 | **Specifying libhydrogen location**: See the `make run_custom_ld` target in 15 | Makefile for example. 16 | 17 | **Note**: If editing / recompiling `libhydrogen` and then running tests, it may 18 | be necessary to build with the `-a` flag to force a rebuild. See `make rebuild` 19 | target in Makefile for example. 20 | 21 | ```sh 22 | # add as go mod dependency 23 | go get github.com/someburner/libhydrogen-go 24 | ``` 25 |
26 | 27 | ```go 28 | // and use in project 29 | package main 30 | 31 | import ( 32 | "fmt" 33 | hydro "github.com/someburner/libhydrogen-go" 34 | ) 35 | 36 | func main() { 37 | fmt.Println(hydro.VersionVerbose()) 38 | } 39 | ``` 40 | 41 | ## Install libhydrogen 42 | 43 | ```sh 44 | # to build inside submodule, or run the examples 45 | git clone --recursive https://github.com/someburner/libhydrogen-go.git 46 | cd libhydrogen-go 47 | 48 | # build/install libhydrogen 49 | cd libhydrogen 50 | make 51 | sudo make install 52 | ``` 53 | 54 |
55 | 56 | ## Examples 57 | 58 | See [example](tests/main.go). Or run with `make`. 59 | 60 | ```sh 61 | make run 62 | ``` 63 | 64 | ## Links 65 | 66 | * cgo [reference](https://golang.org/cmd/cgo/) 67 | 68 | ## Credits 69 | 70 | Several methods in `core.go` taken from `libsodium-go`. 71 | -------------------------------------------------------------------------------- /core.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | // #cgo LDFLAGS: -lhydrogen 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "fmt" 10 | "unsafe" 11 | ) 12 | 13 | func init() { 14 | result := int(C.hydro_init()) 15 | if result != 0 { 16 | panic(fmt.Sprintf("hydrogen initialization failed, result code %d.", result)) 17 | } 18 | // fmt.Println("libhydrogen initialized") 19 | } 20 | 21 | func CheckCtx(ctx string, wantlen int) { 22 | if len(ctx) != wantlen { 23 | panic(fmt.Sprintf("Bad context len. Want (%d), got (%d).", wantlen, len(ctx))) 24 | } 25 | } 26 | 27 | // CheckSize checks if the length of a byte slice is equal to the expected length, 28 | // and panics when this is not the case. 29 | func CheckSize(buf []byte, expected int, descrip string) { 30 | if len(buf) != expected { 31 | panic(fmt.Sprintf("Invalid \"%s\" size. Want (%d), got (%d).", descrip, expected, len(buf))) 32 | } 33 | } 34 | 35 | // CheckIntInRange checks if the size of an integer is between a lower and upper boundaries. 36 | func CheckIntInRange(n int, min int, max int, descrip string) { 37 | if n < min || n > max { 38 | panic(fmt.Sprintf("Invalid \"%s\" size. Want (%d - %d), got (%d).", descrip, min, max, n)) 39 | } 40 | } 41 | 42 | // CheckIntGt checks if n is > lower 43 | func CheckIntGt(n int, lower int, descrip string) { 44 | if !(n > lower) { 45 | panic(fmt.Sprintf("%s is not > %d", descrip, lower)) 46 | } 47 | } 48 | 49 | // CheckIntMin checks if n is > lower 50 | func CheckIntGtOrEq(n int, lower int, descrip string) { 51 | if !(n >= lower) { 52 | panic(fmt.Sprintf("%s is not >= %d", descrip, lower)) 53 | } 54 | } 55 | 56 | // MemZero sets the buffer to zero 57 | func MemZero(buf []byte) { 58 | if len(buf) > 0 { 59 | C.hydro_memzero(unsafe.Pointer(&buf[0]), C.size_t(len(buf))) 60 | } 61 | } 62 | 63 | // NOTE: not a lexicographic comparator, not a replacement for memcmp 64 | // bool hydro_equal(const void *b1_, const void *b2_, size_t len); 65 | func MemEqual(buff1, buff2 []byte, length int) bool { 66 | if length != len(buff1) || length != len(buff2) { 67 | panic(fmt.Sprintf("MemEqual: One or more buf lens (%d, %d) != %d", 68 | len(buff1), len(buff2), length)) 69 | } 70 | return bool(C.hydro_equal(unsafe.Pointer(&buff1[0]), unsafe.Pointer(&buff2[0]), C.size_t(length))) 71 | } 72 | 73 | func Bin2hex(bin []byte) string { 74 | maxlen := len(bin)*2 + 1 75 | binPtr := (*C.uchar)(unsafe.Pointer(&bin[0])) 76 | buf := (*C.char)(C.malloc(C.size_t(maxlen))) 77 | defer C.free(unsafe.Pointer(buf)) 78 | 79 | C.hydro_bin2hex(buf, C.size_t(maxlen), binPtr, C.size_t(len(bin))) 80 | 81 | return C.GoString(buf) 82 | } 83 | 84 | // AlignedSlice returns a memory aligned slice 85 | func AlignedSlice(size, alignment int) []byte { 86 | slice := make([]byte, size+alignment) 87 | offset := alignment - int(uintptr(unsafe.Pointer(&slice[0])))%alignment 88 | return slice[offset : offset+size] 89 | } 90 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/someburner/libhydrogen-go 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /hash.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | // #cgo LDFLAGS: -lhydrogen 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | HashBytes int = C.hydro_hash_BYTES 14 | HashBytesMax int = C.hydro_hash_BYTES_MAX 15 | HashBytesMin int = C.hydro_hash_BYTES_MIN 16 | HashContextBytes int = C.hydro_hash_CONTEXTBYTES 17 | HashKeyBytes int = C.hydro_hash_KEYBYTES 18 | ) 19 | 20 | // Prototype: 21 | // void hydro_hash_keygen(uint8_t *key); 22 | func HashKeygen() []byte { 23 | out := make([]byte, HashKeyBytes) 24 | C.hydro_hash_keygen((*C.uint8_t)(&out[0])) 25 | return out 26 | } 27 | 28 | // Prototype: 29 | // int hydro_hash_hash(uint8_t *out, size_t out_len, const void *in_, size_t in_len, const char ctx[hydro_hash_CONTEXTBYTES], const uint8_t *key); 30 | func HashHash(out_len int, data []byte, ctx string, key []byte) ([]byte, int) { 31 | CheckCtx(ctx, HashContextBytes) 32 | CheckIntInRange(out_len, HashBytesMin, HashBytesMax, "hash out_len") 33 | data_len := len(data) 34 | cCtx := []byte(ctx) 35 | out := make([]byte, out_len) 36 | 37 | var exit int 38 | if key != nil { 39 | exit = int(C.hydro_hash_hash( 40 | (*C.uint8_t)(&out[0]), 41 | (C.size_t)(out_len), 42 | unsafe.Pointer(&data[0]), 43 | (C.size_t)(data_len), 44 | (*C.char)(unsafe.Pointer(&cCtx[0])), 45 | (*C.uint8_t)(&key[0]))) 46 | } else { 47 | exit = int(C.hydro_hash_hash( 48 | (*C.uint8_t)(&out[0]), 49 | (C.size_t)(out_len), 50 | unsafe.Pointer(&data[0]), 51 | (C.size_t)(data_len), 52 | (*C.char)(unsafe.Pointer(&cCtx[0])), 53 | nil)) 54 | } 55 | return out, exit 56 | } 57 | 58 | /* --------------------------------- Multi ---------------------------------- */ 59 | 60 | // 61 | // TODO: detached methods 62 | // 63 | 64 | type HashState struct { 65 | inner *C.hydro_hash_state 66 | } 67 | 68 | type HashHelper struct { 69 | state HashState 70 | context string 71 | } 72 | 73 | // Create a new HashState object. Does not initialize it. 74 | func NewHashState() HashState { 75 | buf := new(C.hydro_hash_state) 76 | out := HashState{buf} 77 | return out 78 | } 79 | 80 | // Prototype: 81 | // int hydro_hash_init(hydro_hash_state *state, const char ctx[hydro_hash_CONTEXTBYTES], const uint8_t key[hydro_hash_KEYBYTES]); 82 | func NewHashHelper(ctx string, key []byte) HashHelper { 83 | CheckCtx(ctx, HashContextBytes) 84 | cCtx := []byte(ctx) 85 | st := NewHashState() 86 | if key != nil { 87 | CheckSize(key, HashKeyBytes, "hashkey") 88 | C.hydro_hash_init( 89 | st.inner, 90 | (*C.char)(unsafe.Pointer(&cCtx[0])), 91 | (*C.uint8_t)(&key[0])) 92 | } else { 93 | C.hydro_hash_init( 94 | st.inner, 95 | (*C.char)(unsafe.Pointer(&cCtx[0])), 96 | nil) 97 | } 98 | return HashHelper{ 99 | state: st, 100 | context: ctx, 101 | } 102 | } 103 | 104 | // Prototype: 105 | // int hydro_hash_update(hydro_hash_state *state, const void *in_, size_t in_len); 106 | func (h *HashHelper) Update(m []byte) { 107 | mlen := len(m) 108 | C.hydro_hash_update( 109 | h.state.inner, 110 | unsafe.Pointer(&m[0]), 111 | (C.size_t)(mlen)) 112 | } 113 | 114 | // Prototype: 115 | // int hydro_hash_final(hydro_hash_state *state, uint8_t *out, size_t out_len); 116 | func (h *HashHelper) Final(out_len int) []byte { 117 | CheckIntInRange(out_len, HashBytesMin, HashBytesMax, "hash out_len") 118 | out := make([]byte, out_len) 119 | C.hydro_hash_final( 120 | h.state.inner, 121 | (*C.uint8_t)(&out[0]), 122 | (C.size_t)(out_len)) 123 | return out 124 | } 125 | -------------------------------------------------------------------------------- /hydrogen.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | import ( 4 | // "fmt" 5 | "errors" 6 | ) 7 | 8 | var ( 9 | ErrAuth = errors.New("hydrogen: Message forged") 10 | ErrOpenBox = errors.New("hydrogen: Can't open box") 11 | ErrOpenSign = errors.New("hydrogen: Signature forged") 12 | ErrDecryptAEAD = errors.New("hydrogen: Can't decrypt message") 13 | ErrPassword = errors.New("hydrogen: Password not matched") 14 | ErrInvalidKey = errors.New("hydrogen: Invalid key") 15 | ) 16 | -------------------------------------------------------------------------------- /hydrogen_test.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | -------------------------------------------------------------------------------- /kdf.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | // #cgo LDFLAGS: -lhydrogen 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | KdfContextBytes int = C.hydro_kdf_CONTEXTBYTES 14 | KdfKeyBytes int = C.hydro_kdf_KEYBYTES 15 | KdfMaxBytes int = C.hydro_kdf_BYTES_MAX 16 | KdfMinBytes int = C.hydro_kdf_BYTES_MIN 17 | ) 18 | 19 | // Prototype: 20 | // void hydro_kdf_keygen(uint8_t key[hydro_kdf_KEYBYTES]); 21 | func KdfKeygen() []byte { 22 | buf := make([]byte, KdfKeyBytes) 23 | C.hydro_kdf_keygen((*C.uint8_t)(&buf[0])) 24 | return buf 25 | } 26 | 27 | // Prototype: 28 | // int hydro_kdf_derive_from_key(uint8_t *subkey, size_t subkey_len, uint64_t subkey_id, const char ctx[hydro_kdf_CONTEXTBYTES], const uint8_t key[hydro_kdf_KEYBYTES]); 29 | func KdfDeriveFromKey(subkey_len int, id uint64, ctx string, master_key []byte) ([]byte, int) { 30 | CheckSize(master_key, KdfKeyBytes, "kdf-master_key") 31 | CheckCtx(ctx, KdfContextBytes) 32 | CheckIntInRange(subkey_len, KdfMinBytes, KdfMaxBytes, "kdf-subkey_len") 33 | cCtx := []byte(ctx) 34 | out := make([]byte, subkey_len) 35 | 36 | exit := int(C.hydro_kdf_derive_from_key( 37 | (*C.uint8_t)(&out[0]), 38 | (C.size_t)(subkey_len), 39 | (C.uint64_t)(id), 40 | (*C.char)(unsafe.Pointer(&cCtx[0])), 41 | (*C.uint8_t)(&master_key[0]))) 42 | 43 | return out, exit 44 | } 45 | -------------------------------------------------------------------------------- /kx.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | // #cgo LDFLAGS: -lhydrogen 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | // n 14 | KxNPacket1Bytes int = C.hydro_kx_N_PACKET1BYTES 15 | // kk 16 | KxKKPacket1Bytes int = C.hydro_kx_KK_PACKET1BYTES 17 | KxKKPacket2Bytes int = C.hydro_kx_KK_PACKET2BYTES 18 | // xx 19 | KxXXPacket1Bytes int = C.hydro_kx_XX_PACKET1BYTES 20 | KxXXPacket2Bytes int = C.hydro_kx_XX_PACKET2BYTES 21 | KxXXPacket3Bytes int = C.hydro_kx_XX_PACKET3BYTES 22 | // keys 23 | KxPublicKeyBytes int = C.hydro_kx_PUBLICKEYBYTES 24 | KxSecretKeyBytes int = C.hydro_kx_SECRETKEYBYTES 25 | KxSessionKeyBytes int = C.hydro_kx_SESSIONKEYBYTES 26 | KxSeedBytes int = C.hydro_kx_SEEDBYTES 27 | KxPskBytes int = C.hydro_kx_PSKBYTES 28 | ) 29 | 30 | /* -------------------------------- KxKeyPair ------------------------------- */ 31 | // Reprentation of hydro_kx_keypair 32 | type KxKeyPair struct { 33 | inner *C.hydro_kx_keypair 34 | } 35 | 36 | func (kp *KxKeyPair) Pk() []byte { 37 | return C.GoBytes(unsafe.Pointer(&kp.inner.pk), C.hydro_kx_PUBLICKEYBYTES) 38 | } 39 | func (kp *KxKeyPair) Sk() []byte { 40 | return C.GoBytes(unsafe.Pointer(&kp.inner.sk), C.hydro_kx_SECRETKEYBYTES) 41 | } 42 | 43 | /* ---------------------------- KxSessionKeyPair ---------------------------- */ 44 | // Reprentation of hydro_kx_session_keypair 45 | type KxSessionKeyPair struct { 46 | inner *C.hydro_kx_session_keypair 47 | } 48 | 49 | func (kp *KxSessionKeyPair) Rx() []byte { 50 | return C.GoBytes(unsafe.Pointer(&kp.inner.rx), C.hydro_kx_SESSIONKEYBYTES) 51 | } 52 | func (kp *KxSessionKeyPair) Tx() []byte { 53 | return C.GoBytes(unsafe.Pointer(&kp.inner.tx), C.hydro_kx_SESSIONKEYBYTES) 54 | } 55 | 56 | /* --------------------------------- Keygen --------------------------------- */ 57 | // void hydro_kx_keygen(hydro_kx_keypair *static_kp); 58 | func KxKeygen() KxKeyPair { 59 | cKxKeypair := new(C.struct_hydro_kx_keypair) 60 | C.hydro_kx_keygen(cKxKeypair) 61 | return KxKeyPair{cKxKeypair} 62 | } 63 | 64 | // void hydro_kx_keygen_deterministic(hydro_kx_keypair *static_kp, const uint8_t seed[hydro_kx_SEEDBYTES]); 65 | func KxKeygenDeterministic(seed []byte) KxKeyPair { 66 | CheckSize(seed, KxSeedBytes, "seed") 67 | cKxKeypair := new(C.struct_hydro_kx_keypair) 68 | C.hydro_kx_keygen_deterministic(cKxKeypair, (*C.uint8_t)(&seed[0])) 69 | return KxKeyPair{cKxKeypair} 70 | } 71 | 72 | /* ----------------------------------- Kx ----------------------------------- */ 73 | // Reprentation of hydro_kx_state 74 | type KxState struct { 75 | inner *C.hydro_kx_state 76 | } 77 | 78 | // Create a new KxState object 79 | func NewKxState() KxState { 80 | buf := new(C.hydro_kx_state) 81 | return KxState{buf} 82 | } 83 | 84 | /* --------------------------------- Kx (N) --------------------------------- */ 85 | // KxN1: Client 86 | // * Computes a key pair using the server's public key 87 | // * Builds a packet packet that has to be sent to the server 88 | // * Returns KxSessionKeyPair, pkt1 slice, 0/-1 (success/error) 89 | // Prototype: 90 | // int hydro_kx_n_1(hydro_kx_session_keypair *kp, uint8_t packet1[hydro_kx_N_PACKET1BYTES], const uint8_t psk[hydro_kx_PSKBYTES], const uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES]); 91 | func KxN1(psk []byte, server_pubkey []byte) (KxSessionKeyPair, []byte, int) { 92 | if psk != nil { 93 | CheckSize(psk, KxPskBytes, "psk") 94 | } 95 | CheckSize(server_pubkey, KxPublicKeyBytes, "server_pubkey") 96 | pkt1 := make([]byte, KxNPacket1Bytes) 97 | cSessionKp := new(C.struct_hydro_kx_session_keypair) 98 | 99 | var exit int 100 | if psk != nil { 101 | exit = int(C.hydro_kx_n_1( 102 | cSessionKp, 103 | (*C.uint8_t)(&pkt1[0]), 104 | (*C.uint8_t)(&psk[0]), 105 | (*C.uint8_t)(&server_pubkey[0]))) 106 | } else { 107 | exit = int(C.hydro_kx_n_1( 108 | cSessionKp, 109 | (*C.uint8_t)(&pkt1[0]), 110 | nil, 111 | (*C.uint8_t)(&server_pubkey[0]))) 112 | } 113 | return KxSessionKeyPair{cSessionKp}, pkt1, exit 114 | } 115 | 116 | // KxN2: Server 117 | // * Process the initial request from the client (aka packet1) 118 | // * Compute sessions keys 119 | // * Returns KxSessionKeyPair, pkt1 slice, 0/-1 (success/error) 120 | // Prototype: 121 | // int hydro_kx_n_2(hydro_kx_session_keypair *kp, const uint8_t packet1[hydro_kx_N_PACKET1BYTES], const uint8_t psk[hydro_kx_PSKBYTES], const hydro_kx_keypair *static_kp); 122 | func KxN2(pkt1 []byte, psk []byte, server_kp KxKeyPair) (KxSessionKeyPair, int) { 123 | if psk != nil { 124 | CheckSize(psk, KxPskBytes, "psk") 125 | } 126 | CheckSize(pkt1, KxNPacket1Bytes, "n2-pkt1") 127 | cSessionKp := new(C.struct_hydro_kx_session_keypair) 128 | 129 | var exit int 130 | if psk != nil { 131 | exit = int(C.hydro_kx_n_2( 132 | cSessionKp, 133 | (*C.uint8_t)(&pkt1[0]), 134 | (*C.uint8_t)(&psk[0]), 135 | server_kp.inner)) 136 | } else { 137 | exit = int(C.hydro_kx_n_2( 138 | cSessionKp, 139 | (*C.uint8_t)(&pkt1[0]), 140 | nil, 141 | server_kp.inner)) 142 | } 143 | return KxSessionKeyPair{cSessionKp}, exit 144 | } 145 | 146 | /* -------------------------------- Kx (KK) --------------------------------- */ 147 | // KxKK1: Client -> Server 148 | // * initializes the local state 149 | // * compute ephemeral key pair + pkt1 150 | // * Returns pkt1 slice, 0/-1 (success/error) 151 | // Prototype: 152 | // int hydro_kx_kk_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_KK_PACKET1BYTES], const uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES], const hydro_kx_keypair *static_kp); 153 | func KxKK1(st_client KxState, server_pubkey []byte, client_kp KxKeyPair) ([]byte, int) { 154 | CheckSize(server_pubkey, KxPublicKeyBytes, "kk1-server_pubkey") 155 | pkt1 := make([]byte, KxKKPacket1Bytes) 156 | 157 | exit := int(C.hydro_kx_kk_1( 158 | st_client.inner, 159 | (*C.uint8_t)(&pkt1[0]), 160 | (*C.uint8_t)(&server_pubkey[0]), 161 | client_kp.inner)) 162 | 163 | return pkt1, exit 164 | } 165 | 166 | // KxKK2: Server -> Client 167 | // * validates the initial request 168 | // * compute ephemeral key pair + pkt2 169 | // * Returns KxSessionKeyPair, pkt2 slice, 0/-1 (success/error) 170 | // Prototype: 171 | // int hydro_kx_kk_2(hydro_kx_session_keypair *kp, uint8_t packet2[hydro_kx_KK_PACKET2BYTES], const uint8_t packet1[hydro_kx_KK_PACKET1BYTES], const uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES], const hydro_kx_keypair *static_kp); 172 | func KxKK2(pkt1 []byte, client_pubkey []byte, server_kp KxKeyPair) (KxSessionKeyPair, []byte, int) { 173 | CheckSize(pkt1, KxKKPacket1Bytes, "kk2-pkt1") 174 | CheckSize(client_pubkey, KxPublicKeyBytes, "kk2-client_pubkey") 175 | pkt2 := make([]byte, KxKKPacket2Bytes) 176 | cSessionKp := new(C.struct_hydro_kx_session_keypair) 177 | 178 | exit := int(C.hydro_kx_kk_2( 179 | cSessionKp, 180 | (*C.uint8_t)(&pkt2[0]), 181 | (*C.uint8_t)(&pkt1[0]), 182 | (*C.uint8_t)(&client_pubkey[0]), 183 | server_kp.inner)) 184 | 185 | return KxSessionKeyPair{cSessionKp}, pkt2, exit 186 | } 187 | 188 | // KxKK3: Client 189 | // * compute session key pair using server pkt2 190 | // * Returns KxSessionKeyPair, pkt2 slice, 0/-1 (success/error) 191 | // Prototype: 192 | // int hydro_kx_kk_3(hydro_kx_state *state, hydro_kx_session_keypair *kp, const uint8_t packet2[hydro_kx_KK_PACKET2BYTES], const hydro_kx_keypair *static_kp); 193 | func KxKK3(st_client KxState, pkt2 []byte, client_kp KxKeyPair) (KxSessionKeyPair, int) { 194 | CheckSize(pkt2, KxKKPacket1Bytes, "kk3-pkt2 bytes") 195 | cSessionKp := new(C.struct_hydro_kx_session_keypair) 196 | 197 | exit := int(C.hydro_kx_kk_3( 198 | st_client.inner, 199 | cSessionKp, 200 | (*C.uint8_t)(&pkt2[0]), 201 | client_kp.inner)) 202 | 203 | return KxSessionKeyPair{cSessionKp}, exit 204 | } 205 | 206 | type KxHelperKK struct { 207 | state KxState 208 | context string 209 | } 210 | 211 | /* -------------------------------- Kx (XX) --------------------------------- */ 212 | // KxXX1: Client -> Server 213 | // * Returns pkt1 slice, 0/-1 (success/error) 214 | // Prototype: 215 | // int hydro_kx_xx_1(hydro_kx_state *state, uint8_t packet1[hydro_kx_XX_PACKET1BYTES], const uint8_t psk[hydro_kx_PSKBYTES]); 216 | func KxXX1(st_client KxState, psk []byte) ([]byte, int) { 217 | if psk != nil { 218 | CheckSize(psk, KxPskBytes, "psk") 219 | } 220 | pkt1 := make([]byte, KxXXPacket1Bytes) 221 | 222 | var exit int 223 | if psk != nil { 224 | exit = int(C.hydro_kx_xx_1( 225 | st_client.inner, 226 | (*C.uint8_t)(&pkt1[0]), 227 | (*C.uint8_t)(&psk[0]))) 228 | } else { 229 | exit = int(C.hydro_kx_xx_1( 230 | st_client.inner, 231 | (*C.uint8_t)(&pkt1[0]), 232 | nil)) 233 | } 234 | 235 | return pkt1, exit 236 | } 237 | 238 | // KxXX2: Server -> Client 239 | // * Returns pkt2 slice, 0/-1 (success/error) 240 | // Prototype: 241 | // int hydro_kx_xx_2(hydro_kx_state *state, uint8_t packet2[hydro_kx_XX_PACKET2BYTES], const uint8_t packet1[hydro_kx_XX_PACKET1BYTES], const uint8_t psk[hydro_kx_PSKBYTES], const hydro_kx_keypair *static_kp); 242 | func KxXX2(st_server KxState, pkt1 []byte, server_kp KxKeyPair, psk []byte) ([]byte, int) { 243 | if psk != nil { 244 | CheckSize(psk, KxPskBytes, "psk") 245 | } 246 | CheckSize(pkt1, KxXXPacket1Bytes, "xx2-pkt1") 247 | pkt2 := make([]byte, KxXXPacket2Bytes) 248 | 249 | var exit int 250 | if psk != nil { 251 | exit = int(C.hydro_kx_xx_2( 252 | st_server.inner, 253 | (*C.uint8_t)(&pkt2[0]), 254 | (*C.uint8_t)(&pkt1[0]), 255 | (*C.uint8_t)(&psk[0]), 256 | server_kp.inner)) 257 | } else { 258 | exit = int(C.hydro_kx_xx_2( 259 | st_server.inner, 260 | (*C.uint8_t)(&pkt2[0]), 261 | (*C.uint8_t)(&pkt1[0]), 262 | nil, 263 | server_kp.inner)) 264 | } 265 | 266 | return pkt2, exit 267 | } 268 | 269 | // KxXX3: Client -> Server 270 | // * Returns client session pair, pkt3 slice, peer publickey, 0/-1 (success/error) 271 | // Prototype: 272 | // int hydro_kx_xx_3(hydro_kx_state *state, hydro_kx_session_keypair *kp, uint8_t packet3[hydro_kx_XX_PACKET3BYTES], uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES], const uint8_t packet2[hydro_kx_XX_PACKET2BYTES], const uint8_t psk[hydro_kx_PSKBYTES], const hydro_kx_keypair *static_kp); 273 | func KxXX3(st_client KxState, pkt2 []byte, client_kp KxKeyPair, psk []byte) (KxSessionKeyPair, []byte, []byte, int) { 274 | if psk != nil { 275 | CheckSize(psk, KxPskBytes, "psk") 276 | } 277 | CheckSize(pkt2, KxXXPacket2Bytes, "xx3-pkt2") 278 | cSessionKpClient := new(C.struct_hydro_kx_session_keypair) 279 | pkt3 := make([]byte, KxXXPacket3Bytes) 280 | peer_pk := make([]byte, KxPublicKeyBytes) 281 | 282 | var exit int 283 | if psk != nil { 284 | exit = int(C.hydro_kx_xx_3( 285 | st_client.inner, 286 | cSessionKpClient, 287 | (*C.uint8_t)(&pkt3[0]), 288 | (*C.uint8_t)(&peer_pk[0]), 289 | (*C.uint8_t)(&pkt2[0]), 290 | (*C.uint8_t)(&psk[0]), 291 | client_kp.inner)) 292 | } else { 293 | exit = int(C.hydro_kx_xx_3( 294 | st_client.inner, 295 | cSessionKpClient, 296 | (*C.uint8_t)(&pkt3[0]), 297 | (*C.uint8_t)(&peer_pk[0]), 298 | (*C.uint8_t)(&pkt2[0]), 299 | nil, 300 | client_kp.inner)) 301 | } 302 | 303 | return KxSessionKeyPair{cSessionKpClient}, pkt3, peer_pk, exit 304 | } 305 | 306 | // KxXX4: Server 307 | // * Returns server session pair, peer publickey, 0/-1 (success/error) 308 | // Prototype: 309 | // int hydro_kx_xx_4(hydro_kx_state *state, hydro_kx_session_keypair *kp, uint8_t peer_static_pk[hydro_kx_PUBLICKEYBYTES], const uint8_t packet3[hydro_kx_XX_PACKET3BYTES], const uint8_t psk[hydro_kx_PSKBYTES]); 310 | func KxXX4(st_server KxState, pkt3 []byte, psk []byte) (KxSessionKeyPair, []byte, int) { 311 | if psk != nil { 312 | CheckSize(psk, KxPskBytes, "xx4-psk") 313 | } 314 | CheckSize(pkt3, KxXXPacket3Bytes, "xx4-pkt3") 315 | cSessionKpServer := new(C.struct_hydro_kx_session_keypair) 316 | peer_pk := make([]byte, KxPublicKeyBytes) 317 | 318 | var exit int 319 | if psk != nil { 320 | exit = int(C.hydro_kx_xx_4( 321 | st_server.inner, 322 | cSessionKpServer, 323 | (*C.uint8_t)(&peer_pk[0]), 324 | (*C.uint8_t)(&pkt3[0]), 325 | (*C.uint8_t)(&psk[0]))) 326 | } else { 327 | exit = int(C.hydro_kx_xx_4( 328 | st_server.inner, 329 | cSessionKpServer, 330 | (*C.uint8_t)(&peer_pk[0]), 331 | (*C.uint8_t)(&pkt3[0]), 332 | nil)) 333 | } 334 | 335 | return KxSessionKeyPair{cSessionKpServer}, peer_pk, exit 336 | } 337 | 338 | type KxHelperXX struct { 339 | state KxState 340 | context string 341 | } 342 | -------------------------------------------------------------------------------- /pwhash.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | // #cgo LDFLAGS: -lhydrogen 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | PwHashContextBytes int = C.hydro_pwhash_CONTEXTBYTES 14 | PwHashMasterKeyBytes int = C.hydro_pwhash_MASTERKEYBYTES 15 | PwHashStoredBytes int = C.hydro_pwhash_STOREDBYTES 16 | 17 | PwHashDeterministicMemLimit int = 0 18 | PwHashDeterministicThreads int = 1 19 | ) 20 | 21 | // Prototype: 22 | // void hydro_pwhash_keygen(uint8_t master_key[hydro_pwhash_MASTERKEYBYTES]); 23 | func PwHashKeygen() []byte { 24 | out := make([]byte, PwHashMasterKeyBytes) 25 | C.hydro_pwhash_keygen((*C.uint8_t)(&out[0])) 26 | return out 27 | } 28 | 29 | // Prototype: 30 | // int hydro_pwhash_deterministic(uint8_t *h, size_t h_len, const char *passwd, size_t passwd_len, const char ctx[hydro_pwhash_CONTEXTBYTES], const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit, size_t memlimit, uint8_t threads); 31 | func PwHashDeterministic(h_len int, passwd string, ctx string, master_key []byte, opslimit uint64) ([]byte, int) { 32 | CheckIntGt(h_len, 0, "h_len") 33 | CheckIntGt(len(passwd), 0, "len(passwd)") 34 | CheckCtx(ctx, PwHashContextBytes) 35 | CheckSize(master_key, PwHashMasterKeyBytes, "len(master_key)") 36 | cCtx := []byte(ctx) 37 | cPasswd := []byte(passwd) 38 | out := make([]byte, h_len) 39 | 40 | exit := int(C.hydro_pwhash_deterministic( 41 | (*C.uint8_t)(&out[0]), 42 | C.size_t(h_len), 43 | (*C.char)(unsafe.Pointer(&cPasswd[0])), 44 | C.size_t(len(passwd)), 45 | (*C.char)(unsafe.Pointer(&cCtx[0])), 46 | (*C.uint8_t)(&master_key[0]), 47 | C.uint64_t(opslimit), 48 | C.size_t(PwHashDeterministicMemLimit), 49 | C.uint8_t(PwHashDeterministicThreads))) 50 | 51 | return out, exit 52 | } 53 | 54 | // Prototype: 55 | // int hydro_pwhash_create(uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd, size_t passwd_len, const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit, size_t memlimit, uint8_t threads); 56 | func PwHashCreate(passwd string, master_key []byte, opslimit uint64, memlimit int, threads uint8) ([]byte, int) { 57 | CheckIntGt(len(passwd), 0, "len(passwd)") 58 | CheckSize(master_key, PwHashMasterKeyBytes, "master_key len") 59 | cPasswd := []byte(passwd) 60 | out := make([]byte, PwHashStoredBytes) 61 | 62 | exit := int(C.hydro_pwhash_create( 63 | (*C.uint8_t)(&out[0]), 64 | (*C.char)(unsafe.Pointer(&cPasswd[0])), 65 | C.size_t(len(passwd)), 66 | (*C.uint8_t)(&master_key[0]), 67 | C.uint64_t(opslimit), 68 | C.size_t(memlimit), 69 | C.uint8_t(threads))) 70 | 71 | return out, exit 72 | } 73 | 74 | // Prototype: 75 | // int hydro_pwhash_verify(const uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd, size_t passwd_len, const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max); 76 | func PwHashVerify(stored []byte, passwd string, master_key []byte, opslimit_max uint64, memlimit_max int, threads_max uint8) int { 77 | CheckIntGt(len(passwd), 0, "len(passwd)") 78 | CheckSize(stored, PwHashStoredBytes, "stored len") 79 | CheckSize(master_key, PwHashMasterKeyBytes, "master_key len") 80 | cPasswd := []byte(passwd) 81 | 82 | exit := int(C.hydro_pwhash_verify( 83 | (*C.uint8_t)(&stored[0]), 84 | (*C.char)(unsafe.Pointer(&cPasswd[0])), 85 | C.size_t(len(passwd)), 86 | (*C.uint8_t)(&master_key[0]), 87 | C.uint64_t(opslimit_max), 88 | C.size_t(memlimit_max), 89 | C.uint8_t(threads_max))) 90 | 91 | return exit 92 | } 93 | 94 | // Prototype: 95 | // int hydro_pwhash_derive_static_key(uint8_t *static_key, size_t static_key_len, const uint8_t stored[hydro_pwhash_STOREDBYTES], const char *passwd, size_t passwd_len, const char ctx[hydro_pwhash_CONTEXTBYTES], const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit_max, size_t memlimit_max, uint8_t threads_max); 96 | func PwHashDeriveStaticKey(static_key_len int, stored []byte, passwd string, ctx string, master_key []byte, opslimit_max uint64, memlimit_max int, threads_max uint8) ([]byte, int) { 97 | CheckIntGt(static_key_len, 0, "len(static_key_len)") 98 | CheckSize(stored, PwHashStoredBytes, "stored len") 99 | CheckIntGt(len(passwd), 0, "len(passwd)") 100 | CheckCtx(ctx, PwHashContextBytes) 101 | CheckSize(master_key, PwHashMasterKeyBytes, "master_key len") 102 | cPasswd := []byte(passwd) 103 | cCtx := []byte(ctx) 104 | static_key := make([]byte, static_key_len) 105 | 106 | exit := int(C.hydro_pwhash_derive_static_key( 107 | (*C.uint8_t)(&static_key[0]), 108 | (C.size_t)(static_key_len), 109 | (*C.uint8_t)(&stored[0]), 110 | (*C.char)(unsafe.Pointer(&cPasswd[0])), 111 | C.size_t(len(passwd)), 112 | (*C.char)(unsafe.Pointer(&cCtx[0])), 113 | (*C.uint8_t)(&master_key[0]), 114 | C.uint64_t(opslimit_max), 115 | C.size_t(memlimit_max), 116 | C.uint8_t(threads_max))) 117 | 118 | return static_key, exit 119 | } 120 | 121 | // Prototype: 122 | // int hydro_pwhash_reencrypt(uint8_t stored[hydro_pwhash_STOREDBYTES], const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], const uint8_t new_master_key[hydro_pwhash_MASTERKEYBYTES]); 123 | func PwHashReEncrypt(stored []byte, master_key []byte, new_master_key []byte) int { 124 | CheckSize(stored, PwHashStoredBytes, "stored len") 125 | CheckSize(master_key, PwHashMasterKeyBytes, "master_key len") 126 | CheckSize(new_master_key, PwHashMasterKeyBytes, "new_master_key len") 127 | 128 | exit := int(C.hydro_pwhash_reencrypt( 129 | (*C.uint8_t)(&stored[0]), 130 | (*C.uint8_t)(&master_key[0]), 131 | (*C.uint8_t)(&new_master_key[0]))) 132 | 133 | return exit 134 | } 135 | 136 | // Prototype: 137 | // int hydro_pwhash_upgrade(uint8_t stored[hydro_pwhash_STOREDBYTES], const uint8_t master_key[hydro_pwhash_MASTERKEYBYTES], uint64_t opslimit, size_t memlimit, uint8_t threads); 138 | func PwHashUpgrade(stored []byte, master_key []byte, opslimit uint64, memlimit int, threads uint8) int { 139 | CheckSize(stored, PwHashStoredBytes, "stored len") 140 | CheckSize(master_key, PwHashMasterKeyBytes, "master_key len") 141 | 142 | exit := int(C.hydro_pwhash_upgrade( 143 | (*C.uint8_t)(&stored[0]), 144 | (*C.uint8_t)(&master_key[0]), 145 | C.uint64_t(opslimit), 146 | C.size_t(memlimit), 147 | C.uint8_t(threads))) 148 | 149 | return exit 150 | } 151 | -------------------------------------------------------------------------------- /random.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | // #cgo LDFLAGS: -lhydrogen 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | RandomSeedBytes int = C.hydro_random_SEEDBYTES 14 | ) 15 | 16 | // returns an unpredicatable value from 0 - 0xffffffff (included) 17 | // Prototype: 18 | // uint32_t hydro_random_u32(void); 19 | func RandomU32() uint32 { 20 | return uint32(C.hydro_random_u32()) 21 | } 22 | 23 | // returns an unpredictable value between 0 and upper_bound (excluded) 24 | // Prototype: 25 | // uint32_t hydro_random_uniform(const uint32_t upper_bound); 26 | func RandomUniform(upper_bound uint32) uint32 { 27 | return uint32(C.hydro_random_uniform(C.uint(upper_bound))) 28 | } 29 | 30 | // Prototype: 31 | // void hydro_random_buf(void *buf, size_t len); 32 | func RandomBuf(l int) []byte { 33 | CheckIntGt(l, 0, "random buf size") 34 | out := make([]byte, l) 35 | C.hydro_random_buf(unsafe.Pointer(&out[0]), C.size_t(l)) 36 | return out 37 | } 38 | 39 | // Prototype: 40 | // void hydro_random_buf_deterministic(void *buf, size_t len, const uint8_t seed[hydro_random_SEEDBYTES]); 41 | func RandomBufDeterministic(l int, seed []byte) []byte { 42 | CheckSize(seed, RandomSeedBytes, "seed") 43 | CheckIntGt(l, 0, "random buf det size") 44 | out := make([]byte, l) 45 | 46 | C.hydro_random_buf_deterministic( 47 | unsafe.Pointer(&out[0]), 48 | C.size_t(l), 49 | (*C.uint8_t)(&seed[0])) 50 | 51 | return out 52 | } 53 | 54 | // Prototype: 55 | // void hydro_random_ratchet(void); 56 | func RandomRatchet() { 57 | C.hydro_random_ratchet() 58 | } 59 | 60 | // Prototype: 61 | // void hydro_random_reseed(void); 62 | func RandomReseed() { 63 | C.hydro_random_reseed() 64 | } 65 | -------------------------------------------------------------------------------- /secretbox.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | // #cgo LDFLAGS: -lhydrogen 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | SecretboxContextBytes int = C.hydro_secretbox_CONTEXTBYTES 14 | SecretboxHeaderBytes int = C.hydro_secretbox_HEADERBYTES 15 | SecretboxKeyBytes int = C.hydro_secretbox_KEYBYTES 16 | SecretboxProbeBytes int = C.hydro_secretbox_PROBEBYTES 17 | ) 18 | 19 | // Prototype: 20 | // void hydro_secretbox_keygen(uint8_t key[hydro_secretbox_KEYBYTES]); 21 | func SecretboxKeygen() []byte { 22 | buf := make([]byte, SecretboxKeyBytes) 23 | C.hydro_secretbox_keygen((*C.uint8_t)(&buf[0])) 24 | return buf 25 | } 26 | 27 | // Prototype: 28 | // int hydro_secretbox_encrypt(uint8_t *c, const void *m_, size_t mlen, uint64_t msg_id, const char ctx[hydro_secretbox_CONTEXTBYTES], const uint8_t key[hydro_secretbox_KEYBYTES]); 29 | func SecretboxEncrypt(m []byte, mid uint64, ctx string, sk []byte) ([]byte, int) { 30 | CheckCtx(ctx, SecretboxContextBytes) 31 | CheckSize(sk, SecretboxKeyBytes, "sk") 32 | mlen := len(m) 33 | CheckIntGt(mlen, 0, "secretbox-enc-mlen") 34 | cCtx := []byte(ctx) 35 | out := make([]byte, mlen+SecretboxHeaderBytes) 36 | 37 | exit := int(C.hydro_secretbox_encrypt( 38 | (*C.uint8_t)(&out[0]), 39 | unsafe.Pointer(&m[0]), 40 | (C.size_t)(mlen), 41 | (C.uint64_t)(mid), 42 | (*C.char)(unsafe.Pointer(&cCtx[0])), 43 | (*C.uint8_t)(&sk[0]))) 44 | 45 | return out, exit 46 | } 47 | 48 | // Prototype: 49 | // int hydro_secretbox_decrypt(void *m_, const uint8_t *c, size_t clen, uint64_t msg_id, const char ctx[hydro_secretbox_CONTEXTBYTES], const uint8_t key[hydro_secretbox_KEYBYTES]) __attribute__((warn_unused_result)); 50 | func SecretboxDecrypt(c []byte, mid uint64, ctx string, sk []byte) ([]byte, int) { 51 | CheckCtx(ctx, SecretboxContextBytes) 52 | CheckSize(sk, SecretboxKeyBytes, "sk") 53 | clen := len(c) 54 | CheckIntGt(clen, SecretboxHeaderBytes, "secretbox-dec-clen") 55 | cCtx := []byte(ctx) 56 | out := make([]byte, clen-SecretboxHeaderBytes) 57 | 58 | exit := int(C.hydro_secretbox_decrypt( 59 | unsafe.Pointer(&out[0]), 60 | (*C.uint8_t)(&c[0]), 61 | (C.size_t)(clen), 62 | (C.uint64_t)(mid), 63 | (*C.char)(unsafe.Pointer(&cCtx[0])), 64 | (*C.uint8_t)(&sk[0]))) 65 | 66 | return out, exit 67 | } 68 | 69 | // Prototype: 70 | // void hydro_secretbox_probe_create(uint8_t probe[hydro_secretbox_PROBEBYTES], const uint8_t *c, size_t c_len, const char ctx[hydro_secretbox_CONTEXTBYTES], const uint8_t key[hydro_secretbox_KEYBYTES]); 71 | func SecretboxProbeCreate(c []byte, ctx string, sk []byte) []byte { 72 | CheckCtx(ctx, SecretboxContextBytes) 73 | CheckSize(sk, SecretboxKeyBytes, "sk") 74 | clen := len(c) 75 | CheckIntGt(clen, 0, "probe-create-clen") 76 | cCtx := []byte(ctx) 77 | probe := make([]byte, SecretboxProbeBytes) 78 | 79 | C.hydro_secretbox_probe_create( 80 | (*C.uint8_t)(&probe[0]), 81 | (*C.uint8_t)(&c[0]), 82 | (C.size_t)(clen), 83 | (*C.char)(unsafe.Pointer(&cCtx[0])), 84 | (*C.uint8_t)(&sk[0])) 85 | 86 | return probe 87 | } 88 | 89 | // Prototype: 90 | // int hydro_secretbox_probe_verify(const uint8_t probe[hydro_secretbox_PROBEBYTES], const uint8_t *c, size_t c_len, const char ctx[hydro_secretbox_CONTEXTBYTES], const uint8_t key[hydro_secretbox_KEYBYTES]) __attribute__((warn_unused_result)); 91 | func SecretboxProbeVerify(probe []byte, c []byte, ctx string, sk []byte) bool { 92 | CheckSize(probe, SecretboxProbeBytes, "probe") 93 | CheckCtx(ctx, SecretboxContextBytes) 94 | CheckSize(sk, SecretboxKeyBytes, "sk") 95 | clen := len(c) 96 | CheckIntGt(clen, 0, "probe-verify-clen") 97 | cCtx := []byte(ctx) 98 | 99 | result := int(C.hydro_secretbox_probe_verify( 100 | (*C.uint8_t)(&probe[0]), 101 | (*C.uint8_t)(&c[0]), 102 | (C.size_t)(clen), 103 | (*C.char)(unsafe.Pointer(&cCtx[0])), 104 | (*C.uint8_t)(&sk[0]))) 105 | 106 | return bool(result == 0) 107 | } 108 | -------------------------------------------------------------------------------- /sign.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | // #cgo LDFLAGS: -lhydrogen 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | SignBytes int = C.hydro_sign_BYTES 14 | SignContextBytes int = C.hydro_sign_CONTEXTBYTES 15 | SignPublicKeyBytes int = C.hydro_sign_PUBLICKEYBYTES 16 | SignSecretKeyBytes int = C.hydro_sign_SECRETKEYBYTES 17 | SignSeedBytes int = C.hydro_sign_SEEDBYTES 18 | ) 19 | 20 | // Reprentation of hydro_sign_keypair 21 | type SignKeypair struct { 22 | inner *C.hydro_sign_keypair 23 | } 24 | 25 | func (kp *SignKeypair) Pk() []byte { 26 | return C.GoBytes(unsafe.Pointer(&kp.inner.pk), C.hydro_sign_PUBLICKEYBYTES) 27 | } 28 | func (kp *SignKeypair) Sk() []byte { 29 | return C.GoBytes(unsafe.Pointer(&kp.inner.sk), C.hydro_sign_SECRETKEYBYTES) 30 | } 31 | 32 | /* --------------------------------- Keygen --------------------------------- */ 33 | // Prototype: 34 | // void hydro_sign_keygen(hydro_sign_keypair *kp); 35 | func SignKeygen() SignKeypair { 36 | cSignKeypair := new(C.struct_hydro_sign_keypair) 37 | C.hydro_sign_keygen(cSignKeypair) 38 | return SignKeypair{cSignKeypair} 39 | } 40 | 41 | // Prototype: 42 | // void hydro_sign_keygen_deterministic(hydro_sign_keypair *kp, const uint8_t seed[hydro_sign_SEEDBYTES]); 43 | func SignKeygenDeterministic(seed []byte) SignKeypair { 44 | CheckSize(seed, SignSeedBytes, "seed") 45 | cSignKeypair := new(C.struct_hydro_sign_keypair) 46 | C.hydro_sign_keygen_deterministic(cSignKeypair, (*C.uint8_t)(&seed[0])) 47 | return SignKeypair{cSignKeypair} 48 | } 49 | 50 | /* --------------------------------- Single --------------------------------- */ 51 | // Prototype: 52 | // int hydro_sign_create(uint8_t csig[hydro_sign_BYTES], const void *m_, size_t mlen, const char ctx[hydro_sign_CONTEXTBYTES], const uint8_t sk[hydro_sign_SECRETKEYBYTES]); 53 | func SignCreate(m []byte, ctx string, sk []byte) ([]byte, int) { 54 | CheckCtx(ctx, SignContextBytes) 55 | CheckSize(sk, SignSecretKeyBytes, "sign sk") 56 | mlen := len(m) 57 | cCtx := []byte(ctx) 58 | out := make([]byte, SignBytes) 59 | 60 | // Returns 0 on success 61 | exit := int(C.hydro_sign_create( 62 | (*C.uint8_t)(&out[0]), 63 | unsafe.Pointer(&m[0]), 64 | (C.size_t)(mlen), 65 | (*C.char)(unsafe.Pointer(&cCtx[0])), 66 | (*C.uint8_t)(&sk[0]))) 67 | 68 | return out, exit 69 | } 70 | 71 | // Prototype: 72 | // int hydro_sign_verify(const uint8_t csig[hydro_sign_BYTES], const void *m_, size_t mlen, const char ctx[hydro_sign_CONTEXTBYTES], const uint8_t pk[hydro_sign_PUBLICKEYBYTES]) _hydro_attr_warn_unused_result_; 73 | func SignVerify(sig []byte, m []byte, ctx string, pk []byte) bool { 74 | CheckSize(sig, SignBytes, "sign sig") 75 | CheckCtx(ctx, SignContextBytes) 76 | CheckSize(pk, SignPublicKeyBytes, "sign pk") 77 | mlen := len(m) 78 | cCtx := []byte(ctx) 79 | 80 | // Returns 0 on success 81 | exit := int(C.hydro_sign_verify( 82 | (*C.uint8_t)(&sig[0]), 83 | unsafe.Pointer(&m[0]), 84 | (C.size_t)(mlen), 85 | (*C.char)(unsafe.Pointer(&cCtx[0])), 86 | (*C.uint8_t)(&pk[0]))) 87 | 88 | return bool(exit == 0) 89 | } 90 | 91 | /* --------------------------------- Multi ---------------------------------- */ 92 | 93 | // 94 | // TODO: detached methods 95 | // 96 | 97 | type SignState struct { 98 | inner *C.hydro_sign_state 99 | } 100 | 101 | type SignHelper struct { 102 | state SignState 103 | context string 104 | } 105 | 106 | // Create a new SignState object. Does not initialize it. 107 | func NewSignState() SignState { 108 | buf := new(C.hydro_sign_state) 109 | out := SignState{buf} 110 | return out 111 | } 112 | 113 | // Prototype: 114 | // int hydro_sign_init(hydro_sign_state *state, const char ctx[hydro_sign_CONTEXTBYTES]); 115 | func NewSignHelper(ctx string) SignHelper { 116 | CheckCtx(ctx, SignContextBytes) 117 | st := NewSignState() 118 | cCtx := []byte(ctx) 119 | 120 | C.hydro_sign_init( 121 | st.inner, 122 | (*C.char)(unsafe.Pointer(&cCtx[0]))) 123 | 124 | return SignHelper{ 125 | state: st, 126 | context: ctx, 127 | } 128 | } 129 | 130 | // Prototype: 131 | // int hydro_sign_update(hydro_sign_state *state, const void *m_, size_t mlen); 132 | func (s *SignHelper) Update(m []byte) { 133 | mlen := len(m) 134 | C.hydro_sign_update( 135 | s.state.inner, 136 | unsafe.Pointer(&m[0]), 137 | (C.size_t)(mlen)) 138 | } 139 | 140 | // Prototype: 141 | // int hydro_sign_final_create(hydro_sign_state *state, uint8_t csig[hydro_sign_BYTES], const uint8_t sk[hydro_sign_SECRETKEYBYTES]); 142 | func (s *SignHelper) FinalCreate(sk []byte, wipe bool) []byte { 143 | CheckSize(sk, SignSecretKeyBytes, "sign sk") 144 | out := make([]byte, SignBytes) 145 | C.hydro_sign_final_create( 146 | s.state.inner, 147 | (*C.uint8_t)(&out[0]), 148 | (*C.uint8_t)(&sk[0])) 149 | return out 150 | } 151 | 152 | // Prototype: 153 | // int hydro_sign_final_verify(hydro_sign_state *state, const uint8_t csig[hydro_sign_BYTES], const uint8_t pk[hydro_sign_PUBLICKEYBYTES]) _hydro_attr_warn_unused_result_; 154 | func (s *SignHelper) FinalVerify(sig []byte, pk []byte) bool { 155 | CheckSize(sig, SignBytes, "sign sig") 156 | CheckSize(pk, SignPublicKeyBytes, "sign pk") 157 | exit := int(C.hydro_sign_final_verify( 158 | s.state.inner, 159 | (*C.uint8_t)(&sig[0]), 160 | (*C.uint8_t)(&pk[0]))) 161 | return bool(exit == 0) 162 | } 163 | -------------------------------------------------------------------------------- /tests/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | hydro "github.com/someburner/libhydrogen-go" 6 | // "testing" 7 | ) 8 | 9 | func main() { 10 | fmt.Println("start") 11 | fmt.Println(hydro.VersionVerbose()) 12 | ExampleHash() 13 | ExampleKdf() 14 | ExampleKx() 15 | ExamplePwHash() 16 | ExampleRandom() 17 | ExampleSecretbox() 18 | ExampleSign() 19 | fmt.Println("\n\nOKAY\nAll Example methods ran to completion\n") 20 | fmt.Println(hydro.VersionVerbose()) 21 | } 22 | 23 | const GOOD_CTX = "goctx123" 24 | const BAD_CTX = "goctx321" 25 | const TEST_MID uint64 = 0 26 | 27 | const TEST_MSG1 = "testing 123" 28 | const TEST_MSG2 = "testing abc" 29 | 30 | const PWHASH_PASSWD = "test" 31 | 32 | func ExampleHash() { 33 | fmt.Printf("\n============= Hash =============\n") 34 | fmt.Printf("HashBytes = %d\n", hydro.HashBytes) 35 | fmt.Printf("HashBytesMax = %d\n", hydro.HashBytesMax) 36 | fmt.Printf("HashBytesMin = %d\n", hydro.HashBytesMin) 37 | fmt.Printf("HashContextBytes = %d\n", hydro.HashContextBytes) 38 | fmt.Printf("HashKeyBytes = %d\n", hydro.HashKeyBytes) 39 | 40 | fmt.Printf("\n--- HashKeygen ---\n") 41 | sk := hydro.HashKeygen() 42 | fmt.Printf("sk [%d] %s\n", len(sk), hydro.Bin2hex(sk)) 43 | 44 | fmt.Printf("\n--- HashHash (w/ key) ---\n") 45 | h1, r1 := hydro.HashHash(hydro.HashBytes, []byte(TEST_MSG1), GOOD_CTX, sk) 46 | if r1 != 0 { 47 | panic("HashHash returned non-zero") 48 | } 49 | fmt.Printf("h1 [%d] %s\n", len(h1), hydro.Bin2hex(h1)) 50 | 51 | fmt.Printf("\n--- HashHash (w/o key) ---\n") 52 | h2, r2 := hydro.HashHash(hydro.HashBytes, []byte(TEST_MSG1), GOOD_CTX, nil) 53 | if r2 != 0 { 54 | panic("HashHash returned non-zero") 55 | } 56 | fmt.Printf("h2 [%d] %s\n", len(h2), hydro.Bin2hex(h2)) 57 | 58 | fmt.Printf("\n--- HashHelper (multi) ---\n") 59 | hh1 := hydro.NewHashHelper(GOOD_CTX, nil) 60 | hh1.Update([]byte(TEST_MSG1)) 61 | hh1.Update([]byte(TEST_MSG2)) 62 | hashmulti1 := hh1.Final(hydro.HashBytes) 63 | fmt.Printf("hashmulti1[%d]:\n%s\n", len(hashmulti1), hydro.Bin2hex(hashmulti1)) 64 | 65 | fmt.Printf("\n--- HashHelper (multi, key) ---\n") 66 | hh2 := hydro.NewHashHelper(GOOD_CTX, sk) 67 | hh2.Update([]byte(TEST_MSG1)) 68 | hh2.Update([]byte(TEST_MSG2)) 69 | hashmulti2 := hh2.Final(hydro.HashBytes) 70 | fmt.Printf("hashmulti2 (key)[%d]:\n%s\n", len(hashmulti2), hydro.Bin2hex(hashmulti2)) 71 | } 72 | 73 | func ExampleKdf() { 74 | fmt.Printf("\n============= Kdf =============\n") 75 | fmt.Printf("KdfContextBytes = %d\n", hydro.KdfContextBytes) 76 | fmt.Printf("KdfKeyBytes = %d\n", hydro.KdfKeyBytes) 77 | fmt.Printf("KdfMaxBytes = %d\n", hydro.KdfMaxBytes) 78 | fmt.Printf("KdfMinBytes = %d\n", hydro.KdfMinBytes) 79 | 80 | fmt.Printf("\n--- KdfKeygen ---\n") 81 | master := hydro.KdfKeygen() 82 | fmt.Printf("master [%d] %s\n", len(master), hydro.Bin2hex(master)) 83 | 84 | fmt.Printf("\n--- KdfDeriveFromKey ---\n") 85 | var id uint64 = 0x0123456789ABCDEF 86 | subkey, _ := hydro.KdfDeriveFromKey(32, id, GOOD_CTX, master) 87 | fmt.Printf("subkey [%d] %s\n", len(subkey), hydro.Bin2hex(subkey)) 88 | } 89 | 90 | func ExampleKxN() { 91 | fmt.Printf("\n============= Kx (N) =============\n") 92 | // long-term server kp 93 | server_kp := hydro.KxKeygen() 94 | 95 | fmt.Printf("\n--- KxN1 (client) ---\n") 96 | sessionKpClient, pkt1, ok := hydro.KxN1(nil, server_kp.Pk()) 97 | if ok != 0 { 98 | panic("KxN1 returned non-zero") 99 | } 100 | fmt.Printf("sessionKpClient\n") 101 | fmt.Printf("[Rx] -> %s\n", hydro.Bin2hex(sessionKpClient.Rx())) 102 | fmt.Printf("[Tx] -> %s\n", hydro.Bin2hex(sessionKpClient.Tx())) 103 | fmt.Printf("[pkt1] -> %s\n", hydro.Bin2hex(pkt1)) 104 | 105 | fmt.Printf("\n--- KxN2 (server) ---\n") 106 | sessionKpServer, ok := hydro.KxN2(pkt1, nil, server_kp) 107 | if ok != 0 { 108 | panic("KxN2 returned non-zero") 109 | } 110 | fmt.Printf("sessionKpServer\n") 111 | fmt.Printf("[Rx] -> %s\n", hydro.Bin2hex(sessionKpServer.Rx())) 112 | fmt.Printf("[Tx] -> %s\n", hydro.Bin2hex(sessionKpServer.Tx())) 113 | 114 | if !hydro.MemEqual(sessionKpClient.Rx(), sessionKpServer.Tx(), hydro.KxSessionKeyBytes) { 115 | panic("Client.Rx != Server.Tx") 116 | } 117 | if !hydro.MemEqual(sessionKpClient.Tx(), sessionKpServer.Rx(), hydro.KxSessionKeyBytes) { 118 | panic("Client.Tx != Server.Rx") 119 | } 120 | fmt.Printf("\nESTABLISHED\n") 121 | } 122 | 123 | func ExampleKxKK() { 124 | fmt.Printf("\n============= Kx (KK) =============\n") 125 | // long-term client, server kp 126 | client_kp := hydro.KxKeygen() 127 | server_kp := hydro.KxKeygen() 128 | 129 | fmt.Printf("\n--- KxKK1 (client) ---\n") 130 | client_st := hydro.NewKxState() 131 | pkt1, kk1res := hydro.KxKK1(client_st, server_kp.Pk(), client_kp) 132 | if kk1res != 0 { 133 | panic("KxKK1 returned non-zero") 134 | } 135 | fmt.Printf("[pkt1] -> %s\n", hydro.Bin2hex(pkt1)) 136 | 137 | fmt.Printf("\n--- KxKK2 (server) ---\n") 138 | sessionKpServer, pkt2, kk2res := hydro.KxKK2(pkt1, client_kp.Pk(), server_kp) 139 | if kk2res != 0 { 140 | panic("KxKK2 returned non-zero") 141 | } 142 | fmt.Printf("sessionKpServer\n") 143 | fmt.Printf("[Rx] -> %s\n", hydro.Bin2hex(sessionKpServer.Rx())) 144 | fmt.Printf("[Tx] -> %s\n", hydro.Bin2hex(sessionKpServer.Tx())) 145 | fmt.Printf("\n[pkt2] -> %s\n", hydro.Bin2hex(pkt2)) 146 | 147 | fmt.Printf("\n--- KxKK3 (client) ---\n") 148 | sessionKpClient, kk3res := hydro.KxKK3(client_st, pkt2, client_kp) 149 | if kk3res != 0 { 150 | panic("KxKK3 returned non-zero") 151 | } 152 | fmt.Printf("sessionKpClient\n") 153 | fmt.Printf("[Rx] -> %s\n", hydro.Bin2hex(sessionKpClient.Rx())) 154 | fmt.Printf("[Tx] -> %s\n", hydro.Bin2hex(sessionKpClient.Tx())) 155 | 156 | if !hydro.MemEqual(sessionKpClient.Rx(), sessionKpServer.Tx(), hydro.KxSessionKeyBytes) { 157 | panic("Client.Rx != Server.Tx") 158 | } 159 | if !hydro.MemEqual(sessionKpClient.Tx(), sessionKpServer.Rx(), hydro.KxSessionKeyBytes) { 160 | panic("Client.Tx != Server.Rx") 161 | } 162 | fmt.Printf("\nESTABLISHED\n") 163 | } 164 | 165 | func ExampleKxXX() { 166 | fmt.Printf("\n============= Kx (XX) =============\n") 167 | // long-term client/server keypairs 168 | client_kp := hydro.KxKeygen() 169 | server_kp := hydro.KxKeygen() 170 | 171 | fmt.Printf("\n--- KxXX1 (client) ---\n") 172 | // init client st 173 | client_st := hydro.NewKxState() 174 | pkt1, xx1res := hydro.KxXX1(client_st, nil) 175 | if xx1res != 0 { 176 | panic("KxXX1 returned non-zero") 177 | } 178 | fmt.Printf("\n[pkt1] -> %s\n", hydro.Bin2hex(pkt1)) 179 | // 180 | // pkt1 --> server 181 | // 182 | fmt.Printf("\n--- KxXX2 (server) ---\n") 183 | // init server st 184 | server_st := hydro.NewKxState() 185 | pkt2, xx2res := hydro.KxXX2(server_st, pkt1, server_kp, nil) 186 | if xx2res != 0 { 187 | panic("KxXX2 returned non-zero") 188 | } 189 | fmt.Printf("\n[pkt2] -> %s\n", hydro.Bin2hex(pkt2)) 190 | // 191 | // pkt2 -> client 192 | // 193 | fmt.Printf("\n--- KxXX3 (client) ---\n") 194 | // func KxXX3(st_client KxState, pkt2 []byte, client_kp KxKeyPair, psk []byte) (KxSessionKeyPair, []byte, []byte, int) 195 | sessionKpClient, pkt3, peerPkClient, xx3res := hydro.KxXX3(client_st, pkt2, client_kp, nil) 196 | if xx3res != 0 { 197 | panic("KxXX3 returned non-zero") 198 | } 199 | fmt.Printf("sessionKpClient:\n") 200 | fmt.Printf("[Rx] -> %s\n", hydro.Bin2hex(sessionKpClient.Rx())) 201 | fmt.Printf("[Tx] -> %s\n", hydro.Bin2hex(sessionKpClient.Tx())) 202 | fmt.Printf("[peerPk] -> %s\n", hydro.Bin2hex(peerPkClient)) 203 | fmt.Printf("\n[pkt3] -> %s\n", hydro.Bin2hex(pkt3)) 204 | // 205 | // pkt3 -> server 206 | // 207 | fmt.Printf("\n--- KxXX4 (server) ---\n") 208 | // func KxXX4(st_server KxState, pkt3 []byte, psk []byte) (KxSessionKeyPair, []byte, int) 209 | sessionKpServer, peerPkServer, xx4res := hydro.KxXX4(server_st, pkt3, nil) 210 | if xx4res != 0 { 211 | panic("KxXX4 returned non-zero") 212 | } 213 | fmt.Printf("sessionKpServer:\n") 214 | fmt.Printf("[Rx] -> %s\n", hydro.Bin2hex(sessionKpServer.Rx())) 215 | fmt.Printf("[Tx] -> %s\n", hydro.Bin2hex(sessionKpServer.Tx())) 216 | fmt.Printf("[peerPk] -> %s\n", hydro.Bin2hex(peerPkServer)) 217 | 218 | if !hydro.MemEqual(sessionKpClient.Rx(), sessionKpServer.Tx(), hydro.KxSessionKeyBytes) { 219 | panic("Client.Rx != Server.Tx") 220 | } 221 | if !hydro.MemEqual(sessionKpClient.Tx(), sessionKpServer.Rx(), hydro.KxSessionKeyBytes) { 222 | panic("Client.Tx != Server.Rx") 223 | } 224 | fmt.Printf("\nESTABLISHED\n") 225 | } 226 | 227 | func ExampleKx() { 228 | fmt.Printf("\n============= Kx =============\n") 229 | fmt.Printf("KxPublicKeyBytes = %d\n", hydro.KxPublicKeyBytes) 230 | fmt.Printf("KxSecretKeyBytes = %d\n", hydro.KxSecretKeyBytes) 231 | fmt.Printf("KxSessionKeyBytes = %d\n", hydro.KxSessionKeyBytes) 232 | fmt.Printf("KxSeedBytes = %d\n", hydro.KxSeedBytes) 233 | 234 | fmt.Printf("\n--- KxKeygen ---\n") 235 | kp := hydro.KxKeygen() 236 | fmt.Printf("[Pk] -> %s\n", hydro.Bin2hex(kp.Pk())) 237 | fmt.Printf("[Sk] -> %s\n", hydro.Bin2hex(kp.Sk())) 238 | 239 | ExampleKxN() 240 | ExampleKxKK() 241 | ExampleKxXX() 242 | } 243 | 244 | func ExamplePwHash() { 245 | fmt.Printf("\n============= PwHash =============\n") 246 | fmt.Printf("PwHashContextBytes = %d\n", hydro.PwHashContextBytes) 247 | fmt.Printf("PwHashMasterKeyBytes = %d\n", hydro.PwHashMasterKeyBytes) 248 | fmt.Printf("PwHashStoredBytes = %d\n", hydro.PwHashStoredBytes) 249 | 250 | fmt.Printf("\n--- PwHashKeygen ---\n") 251 | master := hydro.PwHashKeygen() 252 | fmt.Printf("master [%d] %s\n", len(master), hydro.Bin2hex(master)) 253 | 254 | var opslimit uint64 = 1 255 | var memlimit int = 0 256 | var threads uint8 = 1 257 | 258 | fmt.Printf("\n--- PwHashDeterministic ---\n") 259 | derived_key, res := hydro.PwHashDeterministic(32, PWHASH_PASSWD, GOOD_CTX, master, opslimit) 260 | if res != 0 { 261 | panic("PwHashDeterministic failed") 262 | } 263 | fmt.Printf("derived_key [%d] %s\n", len(derived_key), hydro.Bin2hex(derived_key)) 264 | 265 | fmt.Printf("\n--- PwHashCreate ---\n") 266 | pwhash1, res := hydro.PwHashCreate(PWHASH_PASSWD, master, opslimit, memlimit, threads) 267 | if res != 0 { 268 | panic("PwHashCreate failed") 269 | } else { 270 | fmt.Printf("pwhash1 [%d] %s\n", len(pwhash1), hydro.Bin2hex(pwhash1)) 271 | } 272 | 273 | fmt.Printf("\n--- PwHashVerify (good) ---\n") 274 | if hydro.PwHashVerify(pwhash1, PWHASH_PASSWD, master, opslimit, memlimit, threads) != 0 { 275 | panic("PwHashVerify failed") 276 | } else { 277 | fmt.Printf("PwHashVerify success\n") 278 | } 279 | fmt.Printf("\n--- PwHashVerify (bad) ---\n") 280 | if hydro.PwHashVerify(pwhash1, "wrong password", master, opslimit, memlimit, threads) != 0 { 281 | fmt.Printf("PwHashVerify failed - OK\n") 282 | } else { 283 | panic("PwHashVerify should have failed") 284 | } 285 | 286 | fmt.Printf("\n--- PwHashDeriveStaticKey ---\n") 287 | static_key, res := hydro.PwHashDeriveStaticKey(32, pwhash1, PWHASH_PASSWD, GOOD_CTX, master, opslimit, memlimit, threads) 288 | if res != 0 { 289 | panic("PwHashDeriveStaticKey failed") 290 | } else { 291 | fmt.Printf("PwHashDeriveStaticKey [%d] %s\n", len(static_key), hydro.Bin2hex(static_key)) 292 | } 293 | 294 | fmt.Printf("\n--- PwHashReEncrypt ---\n") 295 | fmt.Printf("PwHashReEncrypt - Before [%d] %s\n", len(pwhash1), hydro.Bin2hex(pwhash1)) 296 | new_master := hydro.PwHashKeygen() 297 | if hydro.PwHashReEncrypt(pwhash1, master, new_master) != 0 { 298 | panic("PwHashReEncrypt failed") 299 | } else { 300 | fmt.Printf("PwHashReEncrypt - After [%d] %s\n", len(pwhash1), hydro.Bin2hex(pwhash1)) 301 | if hydro.PwHashVerify(pwhash1, PWHASH_PASSWD, new_master, opslimit, memlimit, threads) != 0 { 302 | panic("PwHashVerify failed after PwHashReEncrypt") 303 | } else { 304 | fmt.Printf("PwHashVerify success after PwHashReEncrypt\n") 305 | } 306 | if hydro.PwHashVerify(pwhash1, PWHASH_PASSWD, master, opslimit, memlimit, threads) != 0 { 307 | fmt.Printf("PwHashVerify failed (using old master) - OK\n") 308 | } else { 309 | panic("PwHashVerify success (using old master) - BAD") 310 | } 311 | } 312 | 313 | fmt.Printf("\n--- PwHashUpgrade ---\n") 314 | var opslimit_new uint64 = 2 315 | var memlimit_new int = 0 316 | var threads_new uint8 = 2 317 | if hydro.PwHashUpgrade(pwhash1, new_master, opslimit_new, memlimit_new, threads_new) != 0 { 318 | panic("PwHashUpgrade failed") 319 | } else { 320 | fmt.Printf("PwHashUpgrade [%d] %s\n", len(pwhash1), hydro.Bin2hex(pwhash1)) 321 | } 322 | } 323 | 324 | func ExampleRandom() { 325 | fmt.Printf("\n============= Random =============\n") 326 | fmt.Printf("RandomSeedBytes = %d\n", hydro.RandomSeedBytes) 327 | 328 | fmt.Printf("\n--- RandomU32 ---\n") 329 | r32 := hydro.RandomU32() 330 | fmt.Printf("U32 -> 0x%X\n", r32) 331 | 332 | fmt.Printf("\n--- RandomUniform ---\n") 333 | var upper uint32 334 | var i uint 335 | for i = 2; i < 31; i++ { 336 | upper = 2 << i 337 | fmt.Printf("\t(%d)-> 0x%X\n", upper, hydro.RandomUniform(upper)) 338 | } 339 | 340 | fmt.Printf("\n--- RandomBuf ---\n") 341 | buf := hydro.RandomBuf(32) 342 | fmt.Printf("bytes[%d]:\n%s\n", len(buf), hydro.Bin2hex(buf)) 343 | } 344 | 345 | func ExampleSecretbox() { 346 | fmt.Printf("\n============= Secretbox =============\n") 347 | fmt.Printf("SecretboxContextBytes = %d\n", hydro.SecretboxContextBytes) 348 | fmt.Printf("SecretboxHeaderBytes = %d\n", hydro.SecretboxHeaderBytes) 349 | fmt.Printf("SecretboxKeyBytes = %d\n", hydro.SecretboxKeyBytes) 350 | fmt.Printf("SecretboxProbeBytes = %d\n", hydro.SecretboxProbeBytes) 351 | 352 | fmt.Printf("\n--- SecretboxKeygen ---\n") 353 | sk := hydro.SecretboxKeygen() 354 | fmt.Printf("sk bytes[%d]:\n%s\n", len(sk), hydro.Bin2hex(sk)) 355 | 356 | fmt.Printf("\n--- SecretboxEncrypt ---\n") 357 | fmt.Printf("Original plaintext --> \"%s\" <--\n", TEST_MSG1) 358 | ctxt, _ := hydro.SecretboxEncrypt([]byte(TEST_MSG1), TEST_MID, GOOD_CTX, sk) 359 | fmt.Printf("CipherText [%d]:\n%s\n", len(ctxt), hydro.Bin2hex(ctxt)) 360 | 361 | fmt.Printf("\n--- SecretboxDecrypt ---\n") 362 | ptxt, _ := hydro.SecretboxDecrypt(ctxt, TEST_MID, GOOD_CTX, sk) 363 | fmt.Printf("Deciphered plaintext --> \"%s\" <--\n", string(ptxt)) 364 | 365 | fmt.Printf("\n--- SecretboxProbeCreate ---\n") 366 | probe := hydro.SecretboxProbeCreate(ctxt, GOOD_CTX, sk) 367 | fmt.Printf("probe bytes [%d]:\n%s\n", len(probe), hydro.Bin2hex(probe)) 368 | 369 | fmt.Printf("\n--- SecretboxProbeCreate ---\n") 370 | probeOk := hydro.SecretboxProbeVerify(probe, ctxt, GOOD_CTX, sk) 371 | fmt.Print("Result = ") 372 | fmt.Println(probeOk) 373 | } 374 | 375 | func ExampleSign() { 376 | fmt.Printf("\n============= Sign =============\n") 377 | fmt.Printf("SignBytes = %d\n", hydro.SignBytes) 378 | fmt.Printf("SignContextBytes = %d\n", hydro.SignContextBytes) 379 | fmt.Printf("SignPublicKeyBytes = %d\n", hydro.SignPublicKeyBytes) 380 | fmt.Printf("SignSecretKeyBytes = %d\n", hydro.SignSecretKeyBytes) 381 | fmt.Printf("SignSeedBytes = %d\n", hydro.SignSeedBytes) 382 | 383 | fmt.Printf("\n--- SignKeygen ---\n") 384 | kp := hydro.SignKeygen() 385 | fmt.Printf("[Pk] -> %s\n", hydro.Bin2hex(kp.Pk())) 386 | fmt.Printf("[Sk] -> %s\n", hydro.Bin2hex(kp.Sk())) 387 | 388 | fmt.Printf("\n--- SignKeygenDeterministic ---\n") 389 | seed := hydro.RandomBuf(hydro.SignSeedBytes) 390 | kpDet := hydro.SignKeygenDeterministic(seed) 391 | fmt.Printf("[Pk] -> %s\n", hydro.Bin2hex(kpDet.Pk())) 392 | fmt.Printf("[Sk] -> %s\n", hydro.Bin2hex(kpDet.Sk())) 393 | 394 | fmt.Printf("\n--- SignCreate (single) ---\n") 395 | fmt.Printf("Create\n") 396 | sig, createErr := hydro.SignCreate([]byte(TEST_MSG1), GOOD_CTX, kp.Sk()) 397 | if createErr != 0 { 398 | panic("SignCreate returned non-zero") 399 | } 400 | // fmt.Printf("sig[%d]:\n%s\n", len(sig), hydro.Bin2hex(sig)) 401 | fmt.Printf("Verify\n") 402 | sigVerified := hydro.SignVerify(sig, []byte(TEST_MSG1), GOOD_CTX, kp.Pk()) 403 | fmt.Print("sigVerified = ") 404 | fmt.Println(sigVerified) 405 | 406 | fmt.Printf("\n--- SignHelper (multi) ---\n") 407 | fmt.Printf("Create\n") 408 | ss1 := hydro.NewSignHelper(GOOD_CTX) 409 | ss1.Update([]byte(TEST_MSG1)) 410 | ss1.Update([]byte(TEST_MSG2)) 411 | sig1 := ss1.FinalCreate(kp.Sk(), false) 412 | // fmt.Printf("sig1[%d]:\n%s\n", len(sig1), hydro.Bin2hex(sig1)) 413 | fmt.Printf("Verify\n") 414 | ss2 := hydro.NewSignHelper(GOOD_CTX) 415 | ss2.Update([]byte(TEST_MSG1)) 416 | ss2.Update([]byte(TEST_MSG2)) 417 | sig1Verified := ss2.FinalVerify(sig1, kp.Pk()) 418 | fmt.Print("sig1Verified = ") 419 | fmt.Println(sig1Verified) 420 | } 421 | 422 | // 423 | // eof 424 | // 425 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | // #cgo LDFLAGS: -lhydrogen 4 | // #include 5 | import "C" 6 | 7 | import ( 8 | "fmt" 9 | "runtime" 10 | ) 11 | 12 | // VersionMajor returns the libhydrogen major version value 13 | func VersionMajor() int { 14 | return C.HYDRO_VERSION_MAJOR 15 | } 16 | 17 | // VersionMinor returns the libhydrogen minor version value 18 | func VersionMinor() int { 19 | return C.HYDRO_VERSION_MINOR 20 | } 21 | 22 | func VersionPair() (int, int) { 23 | return VersionMajor(), VersionMinor() 24 | } 25 | 26 | func VersionString() string { 27 | return fmt.Sprintf("%d.%d", VersionMajor(), VersionMinor()) 28 | } 29 | 30 | func VersionVerbose() string { 31 | return fmt.Sprintf("libhydrogen v%s - (built with %s)", VersionString(), runtime.Version()) 32 | } 33 | -------------------------------------------------------------------------------- /version_test.go: -------------------------------------------------------------------------------- 1 | package hydrogen 2 | 3 | import ( 4 | hydro "github.com/someburner/libhydrogen-go" 5 | "testing" 6 | ) 7 | 8 | const VersionMajorExpect = 1 9 | const VersionMinorExpect = 0 10 | 11 | func TestVersionPair(t *testing.T) { 12 | maj, min := hydro.VersionPair() 13 | if maj != VersionMajorExpect { 14 | t.Errorf("VersionMajor: Got %d (expected %d).", maj, VersionMajorExpect) 15 | } 16 | if min != VersionMinorExpect { 17 | t.Errorf("VersionMinor: Got %d (expected %d).", min, VersionMinorExpect) 18 | } 19 | } 20 | --------------------------------------------------------------------------------