├── .gitignore ├── LICENSE ├── LICENSE.original ├── README.md ├── README.original.md ├── aead_chacha20poly1305.go ├── aead_xchacha20poly1305.go ├── auth.go ├── box.go ├── core.go ├── exchange.go ├── generichash.go ├── go.mod ├── kdf.go ├── pwhash.go ├── runtime.go ├── scalarmult.go ├── secretbox.go ├── secretstream.go ├── shorthash.go ├── sign.go ├── sodium.go ├── sodium_test.go ├── support.go └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 James Ruan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.original: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 3 | * GoKillers 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sodium 2 | A wrapper for [libsodium](https://github.com/jedisct1/libsodium) in golang 3 | 4 | See documents [![GoDoc](https://godoc.org/github.com/jamesruan/sodium?status.svg)](https://godoc.org/github.com/jamesruan/sodium). 5 | 6 | Currently this is build against libsodium 1.0.18. 7 | 8 | Following functions included: 9 | - `crypto_auth` `crypto_auth_verify` 10 | - `crypto_sign_keypair` `crypto_sign_seed_keypair` `crypto_sign_ed25519_sk_to_seed` `crypto_sign_ed25519_sk_to_pk` 11 | - `crypto_sign` `crypto_sign_open` `crypto_sign_detached` `crypto_sign_verify_detached` 12 | - `crypto_sign_init` `crypto_sign_update` `crypto_sign_final_create` `crypto_sign_final_verify` 13 | - `crypto_sign_ed25519_sk_to_curve25519` `crypto_sign_ed25519_pk_to_curve25519` 14 | - `crypto_scalarmult_base` `crypto_scalarmult` 15 | - `crypto_box_keypair` `crypto_box_seed_keypair` 16 | - `crypto_box_seal` `crypto_box_seal_open` 17 | - `crypto_box_easy` `crypto_box_open_easy` `crypto_box_detached` `crypto_box_open_detached` 18 | - `crypto_secretbox_easy` `crypto_secretbox_open_easy` `crypto_secretbox_detached` `crypto_secretbox_open_detached` 19 | - `crypto_pwhash` `crypto_pwhash_str` `crypto_pwhash_str_verify` 20 | - `crypto_pwhash_opslimit_interactive` `crypto_pwhash_memlimit_interactive` 21 | - `crypto_pwhash_opslimit_moderate` `crypto_pwhash_memlimit_moderate` 22 | - `crypto_pwhash_opslimit_sensitive` `crypto_pwhash_memlimit_sensitive` 23 | - `crypto_shorthash` `crypto_generichash_init` `crypto_generichash_update` `crypto_generichash_final` 24 | - `crypto_kdf_keygen` `crypto_kdf_derive_from_key` 25 | - `crypto_kx_keypair` `crypto_kx_seed_keypair` `crypto_kx_server_session_keys` `crypto_kx_client_session_keys` 26 | - `crypto_aead_chacha20poly1305_ietf_keygen` `crypto_aead_chacha20poly1305_ietf_encrypt` `crypto_aead_chacha20poly1305_ietf_decrypt` 27 | - `crypto_aead_chacha20poly1305_ietf_encrypt_detached` `crypto_aead_chacha20poly1305_ietf_decrypt_detached` 28 | - `crypto_aead_xchacha20poly1305_ietf_keygen` `crypto_aead_xchacha20poly1305_ietf_encrypt` `crypto_aead_xchacha20poly1305_ietf_decrypt` 29 | - `crypto_aead_xchacha20poly1305_ietf_encrypt_detached` `crypto_aead_xchacha20poly1305_ietf_decrypt_detached` 30 | - `crypto_secretstream_xchacha20poly1305_keygen` `crypto_secretstream_xchacha20poly1305_push_init` `crypto_secretstream_xchacha20poly1305_push` 31 | - `crypto_secretstream_xchacha20poly1305_pull_init` `crypto_secretstream_xchacha20poly1305_pull` 32 | - `sodium_memzero` `sodium_memcmp` `sodium_increment` 33 | 34 | > NOTE: This is a modified and enhanced version based on [github.com/GoKillers/libsodium-go](https://github.com/GoKillers/libsodium-go). 35 | > Because there're a lot of package reformat and interface changes, I'd like to launch a new project. 36 | > Thankfully, the original author permits reuse its code as long as the original LICENSE remains. 37 | > You can find the LICENSE.original and README.original.md stating the original license. 38 | > And this version is released under MIT License. 39 | 40 | -------------------------------------------------------------------------------- /README.original.md: -------------------------------------------------------------------------------- 1 | libsodium-go 2 | ============ 3 | A binding library made in Go for the popular portable cryptography library [Sodium](https://download.libsodium.org/doc/). 4 | 5 | 6 | Purpose 7 | ------- 8 | The goal of this binding library is to make use of Sodium in a more Go friendly matter. And of course making it easier to make secure software. 9 | 10 | Team (as of now...) 11 | ---------------- 12 | 16 | 17 | How to build 18 | ------------ 19 | For linux, this should be easy since there's pkg-config support. Please make sure libsodium is installed on your system first. 20 | 21 | 1. `go get -d github.com/GoKillers/libsodium-go` 22 | 2. `cd $GOPATH/src/github.com/GoKillers/libsodium-go` 23 | 3. `./build.sh` 24 | 25 | For Windows, this requires a little more work. 26 | 27 | 1. Download and install pkg-config for [win32](http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/) or [win64](http://ftp.gnome.org/pub/gnome/binaries/win64/dependencies/) 28 | 2. Add a system or user variable PKG_CONFIG_PATH pointing to a folder containing pkg-config files, including libsodium 29 | 3. `go get -d github.com/GoKillers/libsodium-go` 30 | 4. `cd %GOPATH%/src/github.com/GoKillers/libsodium-go` 31 | 5. `build.bat` 32 | 33 | License 34 | --------- 35 | Copyright 2015 - GoKillers 36 | -------------------------------------------------------------------------------- /aead_chacha20poly1305.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | cryptoAEADChaCha20Poly1305IETFKeyBytes = int(C.crypto_aead_chacha20poly1305_ietf_keybytes()) 10 | cryptoAEADChaCha20Poly1305IETFNPubBytes = int(C.crypto_aead_chacha20poly1305_ietf_npubbytes()) 11 | cryptoAEADChaCha20Poly1305IETFABytes = int(C.crypto_aead_chacha20poly1305_ietf_abytes()) 12 | ) 13 | 14 | type AEADCPNonce struct { 15 | Bytes 16 | } 17 | 18 | func (AEADCPNonce) Size() int { 19 | return cryptoAEADChaCha20Poly1305IETFNPubBytes 20 | } 21 | 22 | func (n *AEADCPNonce) Next() { 23 | C.sodium_increment((*C.uchar)(&n.Bytes[0]), (C.size_t)(cryptoAEADChaCha20Poly1305IETFNPubBytes)) 24 | } 25 | 26 | type AEADCPKey struct { 27 | Bytes 28 | } 29 | 30 | func (AEADCPKey) Size() int { 31 | return cryptoAEADChaCha20Poly1305IETFKeyBytes 32 | } 33 | 34 | func MakeAEADCPKey() AEADCPKey { 35 | b := make([]byte, cryptoAEADChaCha20Poly1305IETFKeyBytes); 36 | C.crypto_aead_chacha20poly1305_ietf_keygen((*C.uchar)(&b[0])) 37 | return AEADCPKey{b} 38 | } 39 | 40 | type AEADCPMAC struct { 41 | Bytes 42 | } 43 | 44 | func (AEADCPMAC) Size() int { 45 | return cryptoAEADChaCha20Poly1305IETFABytes 46 | } 47 | 48 | //AEADCPEncrypt encrypts message with AEADCPKey, and AEADCPNonce. 49 | //Message then authenticated with additional data 'ad'. 50 | //Authentication tag is append to the encrypted data. 51 | func (b Bytes) AEADCPEncrypt(ad Bytes, n AEADCPNonce, k AEADCPKey) (c Bytes) { 52 | checkTypedSize(&n, "public nonce") 53 | checkTypedSize(&k, "secret key") 54 | 55 | bp, bl := plen(b) 56 | c = make([]byte, bl+cryptoAEADChaCha20Poly1305IETFABytes) 57 | cp, _ := plen(c) 58 | 59 | var outlen C.ulonglong 60 | 61 | adp, adl := plen(ad) 62 | 63 | if int(C.crypto_aead_chacha20poly1305_ietf_encrypt( 64 | (*C.uchar)(cp), 65 | &outlen, 66 | (*C.uchar)(bp), 67 | (C.ulonglong)(bl), 68 | (*C.uchar)(adp), 69 | (C.ulonglong)(adl), 70 | (*C.uchar)(nil), 71 | (*C.uchar)(&n.Bytes[0]), 72 | (*C.uchar)(&k.Bytes[0]))) != 0 { 73 | panic("see libsodium") 74 | } 75 | c = c[:outlen] 76 | 77 | return 78 | } 79 | 80 | //AEADCPDecrypt decrypts message with AEADCPKey, and AEADCPNonce. 81 | //The appended authenticated tag is verified with additional data 'ad' before decryption. 82 | // 83 | //It returns an error if decryption failed. 84 | func (b Bytes) AEADCPDecrypt(ad Bytes, n AEADCPNonce, k AEADCPKey) (m Bytes, err error) { 85 | checkTypedSize(&n, "public nonce") 86 | checkTypedSize(&k, "secret key") 87 | bp, bl := plen(b) 88 | m = make([]byte, bl-cryptoAEADChaCha20Poly1305IETFABytes) 89 | mp, _ := plen(m) 90 | adp, adl := plen(ad) 91 | 92 | var outlen C.ulonglong 93 | 94 | if int(C.crypto_aead_chacha20poly1305_ietf_decrypt( 95 | (*C.uchar)(mp), 96 | &outlen, 97 | (*C.uchar)(nil), 98 | (*C.uchar)(bp), 99 | (C.ulonglong)(bl), 100 | (*C.uchar)(adp), 101 | (C.ulonglong)(adl), 102 | (*C.uchar)(&n.Bytes[0]), 103 | (*C.uchar)(&k.Bytes[0]))) != 0 { 104 | err = ErrDecryptAEAD 105 | } 106 | m = m[:outlen] 107 | return 108 | } 109 | 110 | //AEADCPEncryptDetached encrypts message with AEADCPKey, and AEADCPNonce. 111 | //Message then authenticated with additional data 'ad'. 112 | //Authentication tag is separated saved as 'mac'. 113 | func (b Bytes) AEADCPEncryptDetached(ad Bytes, n AEADCPNonce, k AEADCPKey) (c Bytes, mac AEADCPMAC) { 114 | checkTypedSize(&n, "public nonce") 115 | checkTypedSize(&k, "secret key") 116 | 117 | bp, bl := plen(b) 118 | adp, adl := plen(ad) 119 | 120 | c = make([]byte, b.Length()) 121 | cp, _ := plen(c) 122 | 123 | macb := make([]byte, cryptoAEADChaCha20Poly1305IETFABytes) 124 | var outlen C.ulonglong 125 | 126 | if int(C.crypto_aead_chacha20poly1305_ietf_encrypt_detached( 127 | (*C.uchar)(cp), 128 | (*C.uchar)(&macb[0]), 129 | &outlen, 130 | (*C.uchar)(bp), 131 | (C.ulonglong)(bl), 132 | (*C.uchar)(adp), 133 | (C.ulonglong)(adl), 134 | (*C.uchar)(nil), 135 | (*C.uchar)(&n.Bytes[0]), 136 | (*C.uchar)(&k.Bytes[0]))) != 0 { 137 | panic("see libsodium") 138 | } 139 | mac = AEADCPMAC{macb[:outlen]} 140 | return 141 | } 142 | 143 | //AEADCPDecryptDetached decrypts message with AEADCPKey, and AEADCPNonce. 144 | //The separated authenticated tag is verified with additional data 'ad' before decryption. 145 | // 146 | //It returns an error if decryption failed. 147 | func (b Bytes) AEADCPDecryptDetached(mac AEADCPMAC, ad Bytes, n AEADCPNonce, k AEADCPKey) (m Bytes, err error) { 148 | checkTypedSize(&mac, "public mac") 149 | checkTypedSize(&n, "public nonce") 150 | checkTypedSize(&k, "secret key") 151 | 152 | bp, bl := plen(b) 153 | adp, adl := plen(ad) 154 | m = make([]byte, bl) 155 | mp, _ := plen(m) 156 | if int(C.crypto_aead_chacha20poly1305_ietf_decrypt_detached( 157 | (*C.uchar)(mp), 158 | (*C.uchar)(nil), 159 | (*C.uchar)(bp), 160 | (C.ulonglong)(bl), 161 | (*C.uchar)(&mac.Bytes[0]), 162 | (*C.uchar)(adp), 163 | (C.ulonglong)(adl), 164 | (*C.uchar)(&n.Bytes[0]), 165 | (*C.uchar)(&k.Bytes[0]))) != 0 { 166 | err = ErrDecryptAEAD 167 | } 168 | return 169 | } 170 | 171 | //AEADCPVerify decrypts message with AEADCPKey, and AEADCPNonce without writing decrypted data. 172 | //The appended authenticated tag is verified with additional data 'ad' before decryption. 173 | // 174 | //It returns an error if decryption failed. 175 | func (b Bytes) AEADCPVerify(ad Bytes, n AEADCPNonce, k AEADCPKey) (err error) { 176 | checkTypedSize(&n, "public nonce") 177 | checkTypedSize(&k, "secret key") 178 | 179 | bp, bl := plen(b) 180 | adp, adl := plen(ad) 181 | 182 | if int(C.crypto_aead_chacha20poly1305_ietf_decrypt( 183 | (*C.uchar)(nil), 184 | (*C.ulonglong)(nil), 185 | (*C.uchar)(nil), 186 | (*C.uchar)(bp), 187 | (C.ulonglong)(bl), 188 | (*C.uchar)(adp), 189 | (C.ulonglong)(adl), 190 | (*C.uchar)(&n.Bytes[0]), 191 | (*C.uchar)(&k.Bytes[0]))) != 0 { 192 | err = ErrDecryptAEAD 193 | } 194 | return 195 | } 196 | 197 | //AEADCPVerifyDetached decrypts message with AEADCPKey, and AEADCPNonce without writing decrypted data. 198 | //The separated authenticated tag is verified with additional data 'ad' before decryption. 199 | // 200 | //It returns an error if decryption failed. 201 | func (b Bytes) AEADCPVerifyDetached(mac AEADCPMAC, ad Bytes, n AEADCPNonce, k AEADCPKey) (err error) { 202 | checkTypedSize(&mac, "public mac") 203 | checkTypedSize(&n, "public nonce") 204 | checkTypedSize(&k, "secret key") 205 | 206 | bp, bl := plen(b) 207 | adp, adl := plen(ad) 208 | 209 | if int(C.crypto_aead_chacha20poly1305_ietf_decrypt_detached( 210 | (*C.uchar)(nil), 211 | (*C.uchar)(nil), 212 | (*C.uchar)(bp), 213 | (C.ulonglong)(bl), 214 | (*C.uchar)(&mac.Bytes[0]), 215 | (*C.uchar)(adp), 216 | (C.ulonglong)(adl), 217 | (*C.uchar)(&n.Bytes[0]), 218 | (*C.uchar)(&k.Bytes[0]))) != 0 { 219 | err = ErrDecryptAEAD 220 | } 221 | return 222 | } 223 | 224 | -------------------------------------------------------------------------------- /aead_xchacha20poly1305.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | cryptoAEADXChaCha20Poly1305IETFKeyBytes = int(C.crypto_aead_xchacha20poly1305_ietf_keybytes()) 10 | cryptoAEADXChaCha20Poly1305IETFNPubBytes = int(C.crypto_aead_xchacha20poly1305_ietf_npubbytes()) 11 | cryptoAEADXChaCha20Poly1305IETFABytes = int(C.crypto_aead_xchacha20poly1305_ietf_abytes()) 12 | ) 13 | 14 | type AEADXCPNonce struct { 15 | Bytes 16 | } 17 | 18 | func (AEADXCPNonce) Size() int { 19 | return cryptoAEADXChaCha20Poly1305IETFNPubBytes 20 | } 21 | 22 | func (n *AEADXCPNonce) Next() { 23 | C.sodium_increment((*C.uchar)(&n.Bytes[0]), (C.size_t)(cryptoAEADChaCha20Poly1305IETFNPubBytes)) 24 | } 25 | 26 | type AEADXCPKey struct { 27 | Bytes 28 | } 29 | 30 | func MakeAEADXCPKey() AEADXCPKey { 31 | b := make([]byte, cryptoAEADXChaCha20Poly1305IETFKeyBytes); 32 | C.crypto_aead_xchacha20poly1305_ietf_keygen((*C.uchar)(&b[0])) 33 | return AEADXCPKey{b} 34 | } 35 | 36 | func (AEADXCPKey) Size() int { 37 | return cryptoAEADXChaCha20Poly1305IETFKeyBytes 38 | } 39 | 40 | type AEADXCPMAC struct { 41 | Bytes 42 | } 43 | 44 | func (AEADXCPMAC) Size() int { 45 | return cryptoAEADXChaCha20Poly1305IETFABytes 46 | } 47 | 48 | //AEADXCPEncrypt encrypts message with AEADXCPKey, and AEADXCPNonce. 49 | //Message then authenticated with additional data 'ad'. 50 | //Authentication tag is append to the encrypted data. 51 | func (b Bytes) AEADXCPEncrypt(ad Bytes, n AEADXCPNonce, k AEADXCPKey) (c Bytes) { 52 | checkTypedSize(&n, "public nonce") 53 | checkTypedSize(&k, "secret key") 54 | 55 | bp, bl := plen(b) 56 | c = make([]byte, bl+cryptoAEADXChaCha20Poly1305IETFABytes) 57 | cp, _ := plen(c) 58 | 59 | var outlen C.ulonglong 60 | 61 | adp, adl := plen(ad) 62 | 63 | if int(C.crypto_aead_xchacha20poly1305_ietf_encrypt( 64 | (*C.uchar)(cp), 65 | &outlen, 66 | (*C.uchar)(bp), 67 | (C.ulonglong)(bl), 68 | (*C.uchar)(adp), 69 | (C.ulonglong)(adl), 70 | (*C.uchar)(nil), 71 | (*C.uchar)(&n.Bytes[0]), 72 | (*C.uchar)(&k.Bytes[0]))) != 0 { 73 | panic("see libsodium") 74 | } 75 | c = c[:outlen] 76 | 77 | return 78 | } 79 | 80 | //AEADXCPDecrypt decrypts message with AEADXCPKey, and AEADXCPNonce. 81 | //The appended authenticated tag is verified with additional data 'ad' before decryption. 82 | // 83 | //It returns an error if decryption failed. 84 | func (b Bytes) AEADXCPDecrypt(ad Bytes, n AEADXCPNonce, k AEADXCPKey) (m Bytes, err error) { 85 | checkTypedSize(&n, "public nonce") 86 | checkTypedSize(&k, "secret key") 87 | bp, bl := plen(b) 88 | m = make([]byte, bl-cryptoAEADXChaCha20Poly1305IETFABytes) 89 | mp, _ := plen(m) 90 | adp, adl := plen(ad) 91 | 92 | var outlen C.ulonglong 93 | 94 | if int(C.crypto_aead_xchacha20poly1305_ietf_decrypt( 95 | (*C.uchar)(mp), 96 | &outlen, 97 | (*C.uchar)(nil), 98 | (*C.uchar)(bp), 99 | (C.ulonglong)(bl), 100 | (*C.uchar)(adp), 101 | (C.ulonglong)(adl), 102 | (*C.uchar)(&n.Bytes[0]), 103 | (*C.uchar)(&k.Bytes[0]))) != 0 { 104 | err = ErrDecryptAEAD 105 | } 106 | m = m[:outlen] 107 | return 108 | } 109 | 110 | //AEADXCPEncryptDetached encrypts message with AEADXCPKey, and AEADXCPNonce. 111 | //Message then authenticated with additional data 'ad'. 112 | //Authentication tag is separated saved as 'mac'. 113 | func (b Bytes) AEADXCPEncryptDetached(ad Bytes, n AEADXCPNonce, k AEADXCPKey) (c Bytes, mac AEADXCPMAC) { 114 | checkTypedSize(&n, "public nonce") 115 | checkTypedSize(&k, "secret key") 116 | 117 | bp, bl := plen(b) 118 | adp, adl := plen(ad) 119 | 120 | c = make([]byte, b.Length()) 121 | cp, _ := plen(c) 122 | 123 | macb := make([]byte, cryptoAEADXChaCha20Poly1305IETFABytes) 124 | var outlen C.ulonglong 125 | 126 | if int(C.crypto_aead_xchacha20poly1305_ietf_encrypt_detached( 127 | (*C.uchar)(cp), 128 | (*C.uchar)(&macb[0]), 129 | &outlen, 130 | (*C.uchar)(bp), 131 | (C.ulonglong)(bl), 132 | (*C.uchar)(adp), 133 | (C.ulonglong)(adl), 134 | (*C.uchar)(nil), 135 | (*C.uchar)(&n.Bytes[0]), 136 | (*C.uchar)(&k.Bytes[0]))) != 0 { 137 | panic("see libsodium") 138 | } 139 | mac = AEADXCPMAC{macb[:outlen]} 140 | return 141 | } 142 | 143 | //AEADXCPDecryptDetached decrypts message with AEADXCPKey, and AEADXCPNonce. 144 | //The separated authenticated tag is verified with additional data 'ad' before decryption. 145 | // 146 | //It returns an error if decryption failed. 147 | func (b Bytes) AEADXCPDecryptDetached(mac AEADXCPMAC, ad Bytes, n AEADXCPNonce, k AEADXCPKey) (m Bytes, err error) { 148 | checkTypedSize(&mac, "public mac") 149 | checkTypedSize(&n, "public nonce") 150 | checkTypedSize(&k, "secret key") 151 | 152 | bp, bl := plen(b) 153 | adp, adl := plen(ad) 154 | m = make([]byte, bl) 155 | mp, _ := plen(m) 156 | if int(C.crypto_aead_xchacha20poly1305_ietf_decrypt_detached( 157 | (*C.uchar)(mp), 158 | (*C.uchar)(nil), 159 | (*C.uchar)(bp), 160 | (C.ulonglong)(bl), 161 | (*C.uchar)(&mac.Bytes[0]), 162 | (*C.uchar)(adp), 163 | (C.ulonglong)(adl), 164 | (*C.uchar)(&n.Bytes[0]), 165 | (*C.uchar)(&k.Bytes[0]))) != 0 { 166 | err = ErrDecryptAEAD 167 | } 168 | return 169 | } 170 | 171 | //AEADXCPVerify decrypts message with AEADXCPKey, and AEADXCPNonce without writing decrypted data. 172 | //The appended authenticated tag is verified with additional data 'ad' before decryption. 173 | // 174 | //It returns an error if decryption failed. 175 | func (b Bytes) AEADXCPVerify(ad Bytes, n AEADXCPNonce, k AEADXCPKey) (err error) { 176 | checkTypedSize(&n, "public nonce") 177 | checkTypedSize(&k, "secret key") 178 | 179 | bp, bl := plen(b) 180 | adp, adl := plen(ad) 181 | 182 | if int(C.crypto_aead_xchacha20poly1305_ietf_decrypt( 183 | (*C.uchar)(nil), 184 | (*C.ulonglong)(nil), 185 | (*C.uchar)(nil), 186 | (*C.uchar)(bp), 187 | (C.ulonglong)(bl), 188 | (*C.uchar)(adp), 189 | (C.ulonglong)(adl), 190 | (*C.uchar)(&n.Bytes[0]), 191 | (*C.uchar)(&k.Bytes[0]))) != 0 { 192 | err = ErrDecryptAEAD 193 | } 194 | return 195 | } 196 | 197 | //AEADXCPVerifyDetached decrypts message with AEADXCPKey, and AEADXCPNonce without writing decrypted data. 198 | //The separated authenticated tag is verified with additional data 'ad' before decryption. 199 | // 200 | //It returns an error if decryption failed. 201 | func (b Bytes) AEADXCPVerifyDetached(mac AEADXCPMAC, ad Bytes, n AEADXCPNonce, k AEADXCPKey) (err error) { 202 | checkTypedSize(&mac, "public mac") 203 | checkTypedSize(&n, "public nonce") 204 | checkTypedSize(&k, "secret key") 205 | 206 | bp, bl := plen(b) 207 | adp, adl := plen(ad) 208 | 209 | if int(C.crypto_aead_xchacha20poly1305_ietf_decrypt_detached( 210 | (*C.uchar)(nil), 211 | (*C.uchar)(nil), 212 | (*C.uchar)(bp), 213 | (C.ulonglong)(bl), 214 | (*C.uchar)(&mac.Bytes[0]), 215 | (*C.uchar)(adp), 216 | (C.ulonglong)(adl), 217 | (*C.uchar)(&n.Bytes[0]), 218 | (*C.uchar)(&k.Bytes[0]))) != 0 { 219 | err = ErrDecryptAEAD 220 | } 221 | return 222 | } 223 | -------------------------------------------------------------------------------- /auth.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | cryptoAuthBytes = int(C.crypto_auth_bytes()) 10 | cryptoAuthKeyBytes = int(C.crypto_auth_keybytes()) 11 | ) 12 | 13 | type MACKey struct { 14 | Bytes 15 | } 16 | 17 | func (b MACKey) Size() int { 18 | return cryptoAuthKeyBytes 19 | } 20 | 21 | //MAC stores Message Authentication Code produced by HMAC-SHA512256. 22 | type MAC struct { 23 | Bytes 24 | } 25 | 26 | func (b MAC) Size() int { 27 | return cryptoAuthBytes 28 | } 29 | 30 | //Auth generates a MAC for the message with the secret 'key'. 31 | func (b Bytes) Auth(key MACKey) (mac MAC) { 32 | checkTypedSize(&key, "Secret Key") 33 | o := make([]byte, cryptoAuthBytes) 34 | 35 | bp, bl := plen(b) 36 | if int(C.crypto_auth( 37 | (*C.uchar)(&o[0]), 38 | (*C.uchar)(bp), 39 | (C.ulonglong)(bl), 40 | (*C.uchar)(&key.Bytes[0]))) != 0 { 41 | panic("see libsodium") 42 | } 43 | mac = MAC{o} 44 | 45 | return 46 | } 47 | 48 | //AuthVerify verifies a messagee with MAC and the secret 'key'. 49 | // 50 | //It returns an error if verification failed. 51 | func (b Bytes) AuthVerify(mac MAC, key MACKey) (err error) { 52 | checkTypedSize(&key, "Secret Key") 53 | checkTypedSize(&mac, "MAC") 54 | 55 | bp, bl := plen(b) 56 | if int(C.crypto_auth_verify( 57 | (*C.uchar)(&mac.Bytes[0]), 58 | (*C.uchar)(bp), 59 | (C.ulonglong)(bl), 60 | (*C.uchar)(&key.Bytes[0]))) != 0 { 61 | err = ErrAuth 62 | } 63 | 64 | return 65 | } 66 | -------------------------------------------------------------------------------- /box.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | cryptoBoxSeedBytes = int(C.crypto_box_seedbytes()) 10 | cryptoBoxPublicKeyBytes = int(C.crypto_box_publickeybytes()) 11 | cryptoBoxSecretKeyBytes = int(C.crypto_box_secretkeybytes()) 12 | cryptoBoxSealBytes = int(C.crypto_box_sealbytes()) 13 | cryptoBoxNonceBytes = int(C.crypto_box_noncebytes()) 14 | cryptoBoxMacBytes = int(C.crypto_box_macbytes()) 15 | ) 16 | 17 | type BoxKP struct { 18 | PublicKey BoxPublicKey 19 | SecretKey BoxSecretKey 20 | } 21 | 22 | type BoxPublicKey struct { 23 | Bytes 24 | } 25 | 26 | func (k BoxPublicKey) Size() int { 27 | return cryptoBoxPublicKeyBytes 28 | } 29 | 30 | type BoxSecretKey struct { 31 | Bytes 32 | } 33 | 34 | func (k BoxSecretKey) Size() int { 35 | return cryptoBoxSecretKeyBytes 36 | } 37 | 38 | //PublicKey calculates public key from BoxSecretKey. 39 | func (k BoxSecretKey) PublicKey() BoxPublicKey { 40 | checkTypedSize(&k, "SecretKey") 41 | 42 | return BoxPublicKey(CryptoScalarmultBase(Scalar(k))) 43 | } 44 | 45 | type BoxSeed struct { 46 | Bytes 47 | } 48 | 49 | func (b BoxSeed) Size() int { 50 | return cryptoBoxSeedBytes 51 | } 52 | 53 | type BoxNonce struct { 54 | Bytes 55 | } 56 | 57 | func (n BoxNonce) Size() int { 58 | return cryptoBoxNonceBytes 59 | } 60 | 61 | func (b *BoxNonce) Next() { 62 | C.sodium_increment((*C.uchar)(&b.Bytes[0]), (C.size_t)(cryptoBoxNonceBytes)) 63 | } 64 | 65 | type BoxMAC struct { 66 | Bytes 67 | } 68 | 69 | func (b BoxMAC) Size() int { 70 | return cryptoBoxMacBytes 71 | } 72 | 73 | //MakeBoxKP generates a keypair for Box 74 | func MakeBoxKP() BoxKP { 75 | pkb := make([]byte, cryptoBoxPublicKeyBytes) 76 | skb := make([]byte, cryptoBoxSecretKeyBytes) 77 | if int(C.crypto_box_keypair( 78 | (*C.uchar)(&pkb[0]), 79 | (*C.uchar)(&skb[0]))) != 0 { 80 | panic("see libsodium") 81 | } 82 | 83 | return BoxKP{ 84 | BoxPublicKey{pkb}, 85 | BoxSecretKey{skb}, 86 | } 87 | } 88 | 89 | //SeedBoxKP generates a keypair for signing from a BoxSeed. 90 | // 91 | //The same pair of keys will be generated with the same 'seed' 92 | func SeedBoxKP(seed BoxSeed) BoxKP { 93 | checkTypedSize(&seed, "seed") 94 | pkb := make([]byte, cryptoBoxPublicKeyBytes) 95 | skb := make([]byte, cryptoBoxSecretKeyBytes) 96 | if int(C.crypto_box_seed_keypair( 97 | (*C.uchar)(&pkb[0]), 98 | (*C.uchar)(&skb[0]), 99 | (*C.uchar)(&seed.Bytes[0]))) != 0 { 100 | panic("see libsodium") 101 | } 102 | 103 | return BoxKP{ 104 | BoxPublicKey{pkb}, 105 | BoxSecretKey{skb}, 106 | } 107 | } 108 | 109 | //SealedBox puts message into a sealed box using receiver's PublicKey and an 110 | //ephemeral key pair of which the SecretKey is destroyed on sender's side 111 | //right after encryption, and the PublicKey is packed with the Box to the 112 | //receiver. 113 | // 114 | //The receiver can open the box but can not verify the identity of the sender. 115 | func (b Bytes) SealedBox(pk BoxPublicKey) (cm Bytes) { 116 | checkTypedSize(&pk, "PublicKey") 117 | bp, bl := plen(b) 118 | cm = make([]byte, b.Length()+cryptoBoxSealBytes) 119 | if int(C.crypto_box_seal( 120 | (*C.uchar)(&cm[0]), 121 | (*C.uchar)(bp), 122 | (C.ulonglong)(bl), 123 | (*C.uchar)(&pk.Bytes[0]))) != 0 { 124 | panic("see libsodium") 125 | } 126 | 127 | return 128 | } 129 | 130 | //SealedBoxOpen reads message from a sealed box using its key pair and ephemeral 131 | //public packed in the Box. 132 | // 133 | //It returns an error if opening failed. 134 | func (b Bytes) SealedBoxOpen(kp BoxKP) (m Bytes, err error) { 135 | checkTypedSize(&kp.PublicKey, "receiver's PublicKey") 136 | checkTypedSize(&kp.SecretKey, "receiver's SecretKey") 137 | bp, bl := plen(b) 138 | m = make([]byte, b.Length()-cryptoBoxSealBytes) 139 | mp, _ := plen(m) 140 | if int(C.crypto_box_seal_open( 141 | (*C.uchar)(mp), 142 | (*C.uchar)(bp), 143 | (C.ulonglong)(bl), 144 | (*C.uchar)(&kp.PublicKey.Bytes[0]), 145 | (*C.uchar)(&kp.SecretKey.Bytes[0]))) != 0 { 146 | err = ErrOpenBox 147 | } 148 | 149 | return 150 | } 151 | 152 | //Box puts message into an authenticated encrypted box using sender's SecretKey 153 | //and receiver's PublicKey, with a shared one-time nonce is used for each 154 | //message. 155 | func (b Bytes) Box(n BoxNonce, pk BoxPublicKey, sk BoxSecretKey) (c Bytes) { 156 | checkTypedSize(&n, "nonce") 157 | checkTypedSize(&pk, "receiver's public key") 158 | checkTypedSize(&sk, "sender's secret key") 159 | bp, bl := plen(b) 160 | c = make([]byte, b.Length()+cryptoBoxMacBytes) 161 | if int(C.crypto_box_easy( 162 | (*C.uchar)(&c[0]), 163 | (*C.uchar)(bp), 164 | (C.ulonglong)(bl), 165 | (*C.uchar)(&n.Bytes[0]), 166 | (*C.uchar)(&pk.Bytes[0]), 167 | (*C.uchar)(&sk.Bytes[0]))) != 0 { 168 | panic("see libsodium") 169 | } 170 | 171 | return 172 | } 173 | 174 | //BoxOpen reads message from an authenticated encrypted box using receiver's 175 | //SecretKey and sender's PublicKey with a shared one-time nonce 176 | // 177 | //It returns an error if opening failed. 178 | func (b Bytes) BoxOpen(n BoxNonce, pk BoxPublicKey, sk BoxSecretKey) (m Bytes, err error) { 179 | checkTypedSize(&n, "nonce") 180 | checkTypedSize(&pk, "receiver's public key") 181 | checkTypedSize(&sk, "sender's secret key") 182 | bp, bl := plen(b) 183 | m = make([]byte, b.Length()-cryptoBoxMacBytes) 184 | mp, _ := plen(m) 185 | if int(C.crypto_box_open_easy( 186 | (*C.uchar)(mp), 187 | (*C.uchar)(bp), 188 | (C.ulonglong)(bl), 189 | (*C.uchar)(&n.Bytes[0]), 190 | (*C.uchar)(&pk.Bytes[0]), 191 | (*C.uchar)(&sk.Bytes[0]))) != 0 { 192 | err = ErrOpenBox 193 | } 194 | 195 | return 196 | } 197 | 198 | //BoxDetached encodes message into an encrypted message using sender's SecretKey 199 | //and receiver's PublicKey, with a shared one-time nonce is used for each 200 | //message. 201 | // 202 | //Detached MAC is return along with encrypted message for authentication. 203 | func (b Bytes) BoxDetached(n BoxNonce, pk BoxPublicKey, sk BoxSecretKey) (mac BoxMAC, c Bytes) { 204 | checkTypedSize(&n, "nonce") 205 | checkTypedSize(&pk, "receiver's public key") 206 | checkTypedSize(&sk, "sender's secret key") 207 | bp, bl := plen(b) 208 | c = make([]byte, bl) 209 | cp, _ := plen(c) 210 | macb := make([]byte, cryptoBoxMacBytes) 211 | if int(C.crypto_box_detached( 212 | (*C.uchar)(cp), 213 | (*C.uchar)(&macb[0]), 214 | (*C.uchar)(bp), 215 | (C.ulonglong)(bl), 216 | (*C.uchar)(&n.Bytes[0]), 217 | (*C.uchar)(&pk.Bytes[0]), 218 | (*C.uchar)(&sk.Bytes[0]))) != 0 { 219 | panic("see libsodium") 220 | } 221 | 222 | return BoxMAC{macb}, c 223 | } 224 | 225 | //BoxOpenDetached decodes message from an encrypted message along with a MAC for 226 | //authentication, and using receiver's SecretKey and sender's PublicKey with 227 | // a shared one-time nonce. 228 | // 229 | //It returns an error if opening failed. 230 | func (b Bytes) BoxOpenDetached(mac BoxMAC, n BoxNonce, pk BoxPublicKey, sk BoxSecretKey) (m Bytes, err error) { 231 | checkTypedSize(&mac, "MAC") 232 | checkTypedSize(&n, "nonce") 233 | checkTypedSize(&pk, "receiver's public key") 234 | checkTypedSize(&sk, "sender's secret key") 235 | bp, bl := plen(b) 236 | m = make([]byte, bl) 237 | mp, _ := plen(m) 238 | if int(C.crypto_box_open_detached( 239 | (*C.uchar)(mp), 240 | (*C.uchar)(bp), 241 | (*C.uchar)(&mac.Bytes[0]), 242 | (C.ulonglong)(bl), 243 | (*C.uchar)(&n.Bytes[0]), 244 | (*C.uchar)(&pk.Bytes[0]), 245 | (*C.uchar)(&sk.Bytes[0]))) != 0 { 246 | err = ErrOpenBox 247 | } 248 | 249 | return 250 | } 251 | -------------------------------------------------------------------------------- /core.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | import "fmt" 4 | 5 | // #cgo pkg-config: libsodium 6 | // #include 7 | // #include 8 | import "C" 9 | 10 | func init() { 11 | result := int(C.sodium_init()) 12 | if result != 0 { 13 | panic(fmt.Sprintf("Sodium initialization failed, result code %d.", 14 | result)) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /exchange.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | cryptoKXPublicKeyBytes = int(C.crypto_kx_publickeybytes()) 10 | cryptoKXSecretKeyBytes = int(C.crypto_kx_secretkeybytes()) 11 | cryptoKXSeedBytes = int(C.crypto_kx_seedbytes()) 12 | cryptoKXSessionKeyBytes = int(C.crypto_kx_sessionkeybytes()) 13 | ) 14 | 15 | type KXKP struct { 16 | PublicKey KXPublicKey 17 | SecretKey KXSecretKey 18 | } 19 | 20 | //MakeKXKP generates a keypair for signing 21 | func MakeKXKP() KXKP { 22 | pkb := make([]byte, cryptoKXPublicKeyBytes) 23 | skb := make([]byte, cryptoKXSecretKeyBytes) 24 | if int(C.crypto_kx_keypair( 25 | (*C.uchar)(&pkb[0]), 26 | (*C.uchar)(&skb[0]))) != 0 { 27 | panic("see libsodium") 28 | } 29 | 30 | return KXKP{ 31 | KXPublicKey{pkb}, 32 | KXSecretKey{skb}, 33 | } 34 | } 35 | 36 | //SeedKXKP generates a keypair for exchanging from a KXSeed. 37 | // 38 | //The same pair of keys will be generated with the same 'seed' 39 | func SeedKXKP(seed KXSeed) KXKP { 40 | checkTypedSize(&seed, "seed") 41 | pkb := make([]byte, cryptoKXPublicKeyBytes) 42 | skb := make([]byte, cryptoKXSecretKeyBytes) 43 | if int(C.crypto_kx_seed_keypair( 44 | (*C.uchar)(&pkb[0]), 45 | (*C.uchar)(&skb[0]), 46 | (*C.uchar)(&seed.Bytes[0]))) != 0 { 47 | panic("see libsodium") 48 | } 49 | 50 | return KXKP{ 51 | KXPublicKey{pkb}, 52 | KXSecretKey{skb}, 53 | } 54 | } 55 | 56 | // ClientSessionKeys calculates Rx (for receving) and Tx (for sending) session keys 57 | // with server's public key. 58 | // return error when server_pk is not acceptable. 59 | func (kp KXKP) ClientSessionKeys(server_pk KXPublicKey) (*KXSessionKeys, error) { 60 | checkTypedSize(&kp.PublicKey, "Client Public Key") 61 | checkTypedSize(&kp.SecretKey, "Client Secret Key") 62 | checkTypedSize(&server_pk, "Server Public Key") 63 | 64 | rxb := make([]byte, cryptoKXSessionKeyBytes) 65 | txb := make([]byte, cryptoKXSessionKeyBytes) 66 | if int(C.crypto_kx_client_session_keys( 67 | (*C.uchar)(&rxb[0]), 68 | (*C.uchar)(&txb[0]), 69 | (*C.uchar)(&kp.PublicKey.Bytes[0]), 70 | (*C.uchar)(&kp.SecretKey.Bytes[0]), 71 | (*C.uchar)(&server_pk.Bytes[0]))) != 0 { 72 | return nil, ErrInvalidKey 73 | } 74 | 75 | return &KXSessionKeys{ 76 | Rx: KXSessionKey{rxb}, 77 | Tx: KXSessionKey{txb}, 78 | }, nil 79 | } 80 | 81 | // ServerSessionKeys calculates Rx (for receving) and Tx (for sending) session keys 82 | // with client's public key. 83 | // return error when client_pk is not acceptable. 84 | func (kp KXKP) ServerSessionKeys(client_pk KXPublicKey) (*KXSessionKeys, error) { 85 | checkTypedSize(&kp.PublicKey, "Server Public Key") 86 | checkTypedSize(&kp.SecretKey, "Server Secret Key") 87 | checkTypedSize(&client_pk, "Client Public Key") 88 | 89 | rxb := make([]byte, cryptoKXSessionKeyBytes) 90 | txb := make([]byte, cryptoKXSessionKeyBytes) 91 | if int(C.crypto_kx_server_session_keys( 92 | (*C.uchar)(&rxb[0]), 93 | (*C.uchar)(&txb[0]), 94 | (*C.uchar)(&kp.PublicKey.Bytes[0]), 95 | (*C.uchar)(&kp.SecretKey.Bytes[0]), 96 | (*C.uchar)(&client_pk.Bytes[0]))) != 0 { 97 | return nil, ErrInvalidKey 98 | } 99 | 100 | return &KXSessionKeys{ 101 | Rx: KXSessionKey{rxb}, 102 | Tx: KXSessionKey{txb}, 103 | }, nil 104 | } 105 | 106 | type KXSessionKeys struct { 107 | Rx KXSessionKey 108 | Tx KXSessionKey 109 | } 110 | 111 | type KXPublicKey struct { 112 | Bytes 113 | } 114 | 115 | func (k KXPublicKey) Size() int { 116 | return cryptoKXPublicKeyBytes 117 | } 118 | 119 | type KXSecretKey struct { 120 | Bytes 121 | } 122 | 123 | func (k KXSecretKey) Size() int { 124 | return cryptoKXSecretKeyBytes 125 | } 126 | 127 | type KXSessionKey struct { 128 | Bytes 129 | } 130 | 131 | func (k KXSessionKey) Size() int { 132 | return cryptoKXSessionKeyBytes 133 | } 134 | 135 | type KXSeed struct { 136 | Bytes 137 | } 138 | 139 | func (k KXSeed) Size() int { 140 | return cryptoKXSeedBytes 141 | } 142 | -------------------------------------------------------------------------------- /generichash.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | import ( 8 | "fmt" 9 | "hash" 10 | ) 11 | 12 | var ( 13 | cryptoGenericHashBytesMin = int(C.crypto_generichash_bytes_min()) 14 | cryptoGenericHashBytesMax = int(C.crypto_generichash_bytes_max()) 15 | cryptoGenericHashKeyBytesMin = int(C.crypto_generichash_keybytes_min()) 16 | cryptoGenericHashKeyBytesMax = int(C.crypto_generichash_keybytes_max()) 17 | cryptoGenericHashBytes = int(C.crypto_generichash_bytes()) 18 | cryptoGenericHashKeyBytes = int(C.crypto_generichash_keybytes()) 19 | cryptoGenericHashPrimitive = C.GoString(C.crypto_generichash_primitive()) 20 | ) 21 | 22 | //GenericHash provides a BLAKE2b (RFC7693) hash, in interface of hash.Hash. 23 | // 24 | //The Hash's and key's size can be any between 16 bytes (128 bits) to 25 | // 64 bytes (512 bits) based on different application. 26 | type GenericHash struct { 27 | size int 28 | blocksize int 29 | key *GenericHashKey 30 | sum []byte 31 | state C.struct_crypto_generichash_blake2b_state 32 | } 33 | 34 | type GenericHashKey struct { 35 | Bytes 36 | } 37 | 38 | func (GenericHashKey) Size() int { 39 | return cryptoGenericHashKeyBytes 40 | } 41 | 42 | //Unkeyed version with default output length. 43 | func NewGenericHashDefault() hash.Hash { 44 | return NewGenericHash(cryptoGenericHashBytes) 45 | } 46 | 47 | //Keyed version with default output length. 48 | func NewGenericHashDefaultKeyed(key GenericHashKey) hash.Hash { 49 | return NewGenericHashKeyed(cryptoGenericHashBytes, key) 50 | } 51 | 52 | //Unkeyed version, output length should between 16 (128-bit) to 64 (512-bit). 53 | func NewGenericHash(outlen int) hash.Hash { 54 | checkSizeInRange(outlen, cryptoGenericHashBytesMin, cryptoGenericHashBytesMax, "out") 55 | hash := GenericHash{ 56 | size: outlen, 57 | blocksize: 128, 58 | key: nil, 59 | sum: nil, 60 | state: C.struct_crypto_generichash_blake2b_state{}, 61 | } 62 | hash.Reset() 63 | return &hash 64 | } 65 | 66 | //Keyed version, output length in bytes should between 16 (128-bit) to 64 (512-bit). 67 | func NewGenericHashKeyed(outlen int, key GenericHashKey) hash.Hash { 68 | checkSizeInRange(outlen, cryptoGenericHashBytesMin, cryptoGenericHashBytesMax, "out") 69 | checkTypedSize(&key, "generic hash key") 70 | hash := GenericHash{ 71 | size: outlen, 72 | blocksize: 128, 73 | key: &key, 74 | sum: nil, 75 | state: C.struct_crypto_generichash_blake2b_state{}, 76 | } 77 | hash.Reset() 78 | return &hash 79 | } 80 | 81 | //Output length in bytes. 82 | // 83 | //Implements hash.Hash 84 | func (g GenericHash) Size() int { 85 | return g.size 86 | } 87 | 88 | //Implements hash.Hash 89 | func (g GenericHash) BlockSize() int { 90 | return g.blocksize 91 | } 92 | 93 | //Implements hash.Hash 94 | func (g *GenericHash) Reset() { 95 | if g.sum != nil { 96 | g.sum = nil 97 | } 98 | if g.key != nil { 99 | if int(C.crypto_generichash_init( 100 | &g.state, 101 | (*C.uchar)(&g.key.Bytes[0]), 102 | (C.size_t)(g.key.Length()), 103 | (C.size_t)(g.size))) != 0 { 104 | panic("see libsodium") 105 | } 106 | } else { 107 | if int(C.crypto_generichash_init( 108 | &g.state, 109 | (*C.uchar)(nil), 110 | (C.size_t)(0), 111 | (C.size_t)(g.size))) != 0 { 112 | panic("see libsodium") 113 | } 114 | } 115 | } 116 | 117 | //Use GenericHash.Write([]byte) to hash chunks of message. 118 | // 119 | //Implements hash.Hash 120 | func (g *GenericHash) Write(p []byte) (n int, err error) { 121 | if g.sum != nil { 122 | return 0, fmt.Errorf("hash finalized") 123 | } 124 | i := p[:] 125 | 126 | for len(i) > g.blocksize { 127 | c := i[:g.blocksize] 128 | i = i[g.blocksize:] 129 | if int(C.crypto_generichash_update( 130 | &g.state, 131 | (*C.uchar)(&c[0]), 132 | (C.ulonglong)(g.blocksize))) != 0 { 133 | panic("see libsodium") 134 | } 135 | } 136 | if len(i) > 0 { 137 | if int(C.crypto_generichash_update( 138 | &g.state, 139 | (*C.uchar)(&i[0]), 140 | (C.ulonglong)(len(i)))) != 0 { 141 | panic("see libsodium") 142 | } 143 | } 144 | return len(p), nil 145 | } 146 | 147 | //Return appended the Sum after b. 148 | // 149 | //Implements hash.Hash. 150 | // 151 | //NOTE: Repeated call is allowed. But can't call Write() after Sum(). 152 | // The underlying state is freed. It MUST be Reset() to use it again after calling Sum(). 153 | // This behaviour is inconsistent with the definition of hash.Hash. 154 | func (g *GenericHash) Sum(b []byte) []byte { 155 | if g.sum != nil { 156 | return append(b, g.sum...) 157 | } 158 | g.sum = make([]byte, g.size) 159 | if int(C.crypto_generichash_final( 160 | &g.state, 161 | (*C.uchar)(&g.sum[0]), 162 | (C.size_t)(g.size))) != 0 { 163 | panic("see libsodium") 164 | } 165 | g.state = C.struct_crypto_generichash_blake2b_state{} 166 | return append(b, g.sum...) 167 | } 168 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jamesruan/sodium 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /kdf.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | import "unsafe" 8 | 9 | var ( 10 | cryptoKDFKeyBytes = int(C.crypto_kdf_keybytes()) 11 | CryptoKDFBytesMin = int(C.crypto_kdf_bytes_min()) 12 | CryptoKDFBytesMax = int(C.crypto_kdf_bytes_max()) 13 | CryptoKDFContextBytes = int(C.crypto_kdf_contextbytes()) 14 | ) 15 | 16 | // MasterKey for deriving SubKeys 17 | type MasterKey struct { 18 | Bytes 19 | } 20 | 21 | func (MasterKey) Size() int { 22 | return cryptoKDFKeyBytes 23 | } 24 | 25 | func (m MasterKey) Length() int { 26 | return len(m.Bytes) 27 | } 28 | 29 | // MakeMasterKey generates a new MasterKey 30 | func MakeMasterKey() MasterKey { 31 | mk := make([]byte, cryptoKDFKeyBytes) 32 | C.crypto_kdf_keygen((*C.uchar)(&mk[0])) 33 | return MasterKey{mk} 34 | } 35 | 36 | // SubKey derived from a MasterKey 37 | type SubKey struct { 38 | Bytes 39 | } 40 | 41 | func (SubKey) Size() int { 42 | return CryptoKDFBytesMax 43 | } 44 | 45 | // KeyContext is a CryptoKDFContextBytes length string indicating 46 | // the context for the key. e.g. "username" 47 | type KeyContext string 48 | 49 | func (k KeyContext) Length() int { 50 | return len(k) 51 | } 52 | 53 | func (KeyContext) Size() int { 54 | return CryptoKDFContextBytes 55 | } 56 | 57 | func (k *KeyContext) setBytes(b Bytes) { 58 | s := make([]byte, CryptoKDFContextBytes) 59 | copy(s, b) 60 | *k = KeyContext(s) 61 | } 62 | 63 | func MakeKeyContext(s string) KeyContext { 64 | c := new(KeyContext) 65 | c.setBytes(Bytes(s)) 66 | return *c 67 | } 68 | 69 | // Derive SubKey from the MasterKey 70 | // length should be between CryptoKDFBytesMin and CryptoKDFBytesMax 71 | func (m MasterKey) Derive(length int, id uint64, context KeyContext) SubKey { 72 | checkSizeInRange(length, CryptoKDFBytesMin, CryptoKDFBytesMax, "deriving subkey") 73 | checkTypedSize(&context, "key context") 74 | sk := make([]byte, length) 75 | ctxc := C.CString(string(context)) 76 | defer C.free(unsafe.Pointer(ctxc)) 77 | 78 | if int(C.crypto_kdf_derive_from_key( 79 | (*C.uchar)(&sk[0]), 80 | (C.size_t)(length), 81 | (C.uint64_t)(id), 82 | ctxc, 83 | (*C.uchar)(&m.Bytes[0]))) != 0 { 84 | panic("see libsodium") 85 | } 86 | 87 | return SubKey{sk} 88 | } 89 | -------------------------------------------------------------------------------- /pwhash.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | import "unsafe" 8 | 9 | var ( 10 | cryptoPWHashSaltBytes = int(C.crypto_pwhash_saltbytes()) 11 | cryptoPWHashStrBytes = int(C.crypto_pwhash_strbytes()) 12 | CryptoPWHashOpsLimitInteractive = int(C.crypto_pwhash_opslimit_interactive()) 13 | CryptoPWHashMemLimitInteractive = int(C.crypto_pwhash_memlimit_interactive()) 14 | CryptoPWHashOpsLimitModerate = int(C.crypto_pwhash_opslimit_moderate()) 15 | CryptoPWHashMemLimitModerate = int(C.crypto_pwhash_memlimit_moderate()) 16 | CryptoPWHashOpsLimitSensitive = int(C.crypto_pwhash_opslimit_sensitive()) 17 | CryptoPWHashMemLimitSensitive = int(C.crypto_pwhash_memlimit_sensitive()) 18 | ) 19 | 20 | // PWHashSalt implements the Typed interface 21 | type PWHashSalt struct { 22 | Bytes 23 | } 24 | 25 | func (s PWHashSalt) Size() int { 26 | return cryptoPWHashSaltBytes 27 | } 28 | 29 | // PWHashStr implements the Typed interface 30 | type PWHashStr struct { 31 | string 32 | } 33 | 34 | func LoadPWHashStr(b Bytes) PWHashStr { 35 | t := new(PWHashStr) 36 | t.setBytes(b) 37 | return *t 38 | } 39 | 40 | // Value returns the underlying bytes for PWHashStr 41 | func (s PWHashStr) Value() Bytes { 42 | return Bytes(s.string) 43 | } 44 | 45 | func (s PWHashStr) Size() int { 46 | return cryptoPWHashStrBytes 47 | } 48 | 49 | func (s PWHashStr) Length() int { 50 | return len(s.string) 51 | } 52 | 53 | func (s *PWHashStr) setBytes(b Bytes) { 54 | t := PWHashStr{string(b[:])} 55 | checkTypedSize(&t, "PWHashStr") 56 | *s = t 57 | } 58 | 59 | //PWHashStore use moderate profile to pack hashed password into PWHashStr. 60 | func PWHashStore(pw string) PWHashStr { 61 | s := make([]C.char, cryptoPWHashStrBytes) 62 | pwc := C.CString(pw) 63 | defer C.free(unsafe.Pointer(pwc)) 64 | 65 | if int(C.crypto_pwhash_str( 66 | &s[0], 67 | pwc, 68 | (C.ulonglong)(len(pw)), 69 | (C.ulonglong)(CryptoPWHashOpsLimitModerate), 70 | (C.size_t)(CryptoPWHashMemLimitModerate))) != 0 { 71 | panic("see libsodium") 72 | } 73 | return PWHashStr{C.GoStringN(&s[0], C.int(cryptoPWHashStrBytes))} 74 | } 75 | 76 | //PWHashStoreSensitive use sensitive profile to pack hashed password into PWHashStr. 77 | func PWHashStoreSensitive(pw string) PWHashStr { 78 | s := make([]C.char, cryptoPWHashStrBytes) 79 | pwc := C.CString(pw) 80 | defer C.free(unsafe.Pointer(pwc)) 81 | 82 | if int(C.crypto_pwhash_str( 83 | &s[0], 84 | pwc, 85 | (C.ulonglong)(len(pw)), 86 | (C.ulonglong)(CryptoPWHashOpsLimitSensitive), 87 | (C.size_t)(CryptoPWHashMemLimitSensitive))) != 0 { 88 | panic("see libsodium") 89 | } 90 | return PWHashStr{C.GoString(&s[0])} 91 | } 92 | 93 | //PWHashStoreInteractive use interactive profile to pack hashed password into PWHashStr. 94 | func PWHashStoreInteractive(pw string) PWHashStr { 95 | s := make([]C.char, cryptoPWHashStrBytes) 96 | pwc := C.CString(pw) 97 | defer C.free(unsafe.Pointer(pwc)) 98 | 99 | if int(C.crypto_pwhash_str( 100 | &s[0], 101 | pwc, 102 | (C.ulonglong)(len(pw)), 103 | (C.ulonglong)(CryptoPWHashOpsLimitInteractive), 104 | (C.size_t)(CryptoPWHashMemLimitInteractive))) != 0 { 105 | panic("see libsodium") 106 | } 107 | return PWHashStr{C.GoString(&s[0])} 108 | } 109 | 110 | //PWHashVerify verifies password. 111 | func (s PWHashStr) PWHashVerify(pw string) (err error) { 112 | sc := C.CString(s.string) 113 | defer C.free(unsafe.Pointer(sc)) 114 | pwc := C.CString(pw) 115 | defer C.free(unsafe.Pointer(pwc)) 116 | if int(C.crypto_pwhash_str_verify( 117 | sc, 118 | pwc, 119 | (C.ulonglong)(len(pw)))) != 0 { 120 | err = ErrPassword 121 | } 122 | return 123 | } 124 | -------------------------------------------------------------------------------- /runtime.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | RuntimeHasNeon = bool(C.sodium_runtime_has_neon() != 0) 10 | RuntimeHasSse2 = bool(C.sodium_runtime_has_sse2() != 0) 11 | RuntimeHasSse3 = bool(C.sodium_runtime_has_sse3() != 0) 12 | ) 13 | -------------------------------------------------------------------------------- /scalarmult.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | cryptoScalarmultBytes = int(C.crypto_scalarmult_bytes()) 10 | cryptoScalarmultScalarBytes = int(C.crypto_scalarmult_scalarbytes()) 11 | ) 12 | 13 | type Scalar struct { 14 | Bytes 15 | } 16 | 17 | func (s Scalar) Size() int { 18 | return cryptoScalarmultScalarBytes 19 | } 20 | 21 | //ScalarMult is the mulitiplication of two Scalar, used to calculate shared key 22 | // from BoxSecertKey and other end's BoxPublicKey. 23 | type ScalarMult struct { 24 | Bytes 25 | } 26 | 27 | func (s ScalarMult) Size() int { 28 | return cryptoScalarmultBytes 29 | } 30 | 31 | //CryptoScalarmultBase calculates BoxPublicKey 'q' from BoxSecertKey 'n'. 32 | func CryptoScalarmultBase(n Scalar) (q Scalar) { 33 | checkTypedSize(&n, "SecretKey") 34 | qb := make([]byte, cryptoScalarmultScalarBytes) 35 | 36 | if int(C.crypto_scalarmult_base( 37 | (*C.uchar)(&qb[0]), 38 | (*C.uchar)(&n.Bytes[0]))) != 0 { 39 | panic("see libsodium") 40 | } 41 | 42 | return Scalar{qb} 43 | } 44 | 45 | //CryptoScalarmult calculates common key 'q' from private key 'n' and 46 | //other's public key 'p' 47 | func CryptoScalarmult(n, p Scalar) (q ScalarMult) { 48 | checkTypedSize(&n, "SecretKey") 49 | checkTypedSize(&p, "PublicKey") 50 | 51 | qb := make([]byte, cryptoScalarmultBytes) 52 | if int(C.crypto_scalarmult( 53 | (*C.uchar)(&qb[0]), 54 | (*C.uchar)(&n.Bytes[0]), 55 | (*C.uchar)(&p.Bytes[0]))) != 0 { 56 | panic("see libsodium") 57 | } 58 | 59 | return ScalarMult{qb} 60 | } 61 | -------------------------------------------------------------------------------- /secretbox.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | cryptoSecretBoxKeyBytes = int(C.crypto_secretbox_keybytes()) 10 | cryptoSecretBoxNonceBytes = int(C.crypto_secretbox_noncebytes()) 11 | cryptoSecretBoxMacBytes = int(C.crypto_secretbox_macbytes()) 12 | ) 13 | 14 | type SecretBoxKey struct { 15 | Bytes 16 | } 17 | 18 | func (s SecretBoxKey) Size() int { 19 | return cryptoSecretBoxKeyBytes 20 | } 21 | 22 | type SecretBoxNonce struct { 23 | Bytes 24 | } 25 | 26 | func (n SecretBoxNonce) Size() int { 27 | return cryptoSecretBoxNonceBytes 28 | } 29 | 30 | func (n *SecretBoxNonce) Next() { 31 | C.sodium_increment((*C.uchar)(&n.Bytes[0]), (C.size_t)(cryptoSecretBoxNonceBytes)) 32 | } 33 | 34 | type SecretBoxMAC struct { 35 | Bytes 36 | } 37 | 38 | func (s SecretBoxMAC) Size() int { 39 | return cryptoSecretBoxMacBytes 40 | } 41 | 42 | //SecretBox use a SecretBoxNonce and a SecretBoxKey to encrypt a message. 43 | func (b Bytes) SecretBox(n SecretBoxNonce, k SecretBoxKey) (c Bytes) { 44 | checkTypedSize(&n, "nonce") 45 | checkTypedSize(&k, "secret key") 46 | 47 | bp, bl := plen(b) 48 | c = make([]byte, bl+cryptoSecretBoxMacBytes) 49 | if int(C.crypto_secretbox_easy( 50 | (*C.uchar)(&c[0]), 51 | (*C.uchar)(bp), 52 | (C.ulonglong)(bl), 53 | (*C.uchar)(&n.Bytes[0]), 54 | (*C.uchar)(&k.Bytes[0]))) != 0 { 55 | panic("see libsodium") 56 | } 57 | 58 | return 59 | } 60 | 61 | //SecretBoxOpen opens a SecretBox using SecretBoxKey and SecretBoxNonce. 62 | // 63 | //It returns an error if opening failed. 64 | func (b Bytes) SecretBoxOpen(n SecretBoxNonce, k SecretBoxKey) (m Bytes, err error) { 65 | checkTypedSize(&n, "nonce") 66 | checkTypedSize(&k, "secret key") 67 | bp, bl := plen(b) 68 | m = make([]byte, bl-cryptoSecretBoxMacBytes) 69 | mp, _ := plen(m) 70 | if int(C.crypto_secretbox_open_easy( 71 | (*C.uchar)(mp), 72 | (*C.uchar)(bp), 73 | (C.ulonglong)(bl), 74 | (*C.uchar)(&n.Bytes[0]), 75 | (*C.uchar)(&k.Bytes[0]))) != 0 { 76 | err = ErrOpenBox 77 | } 78 | 79 | return 80 | } 81 | 82 | //SecretBoxDetached use a SecretBoxNonce and a SecretBoxKey to encrypt a message. 83 | //A separate MAC is returned. 84 | func (b Bytes) SecretBoxDetached(n SecretBoxNonce, k SecretBoxKey) (c Bytes, mac SecretBoxMAC) { 85 | checkTypedSize(&n, "nonce") 86 | checkTypedSize(&k, "secret key") 87 | bp, bl := plen(b) 88 | c = make([]byte, bl) 89 | cp, _ := plen(c) 90 | macb := make([]byte, cryptoSecretBoxMacBytes) 91 | if int(C.crypto_secretbox_detached( 92 | (*C.uchar)(cp), 93 | (*C.uchar)(&macb[0]), 94 | (*C.uchar)(bp), 95 | (C.ulonglong)(bl), 96 | (*C.uchar)(&n.Bytes[0]), 97 | (*C.uchar)(&k.Bytes[0]))) != 0 { 98 | panic("see libsodium") 99 | } 100 | mac = SecretBoxMAC{macb} 101 | 102 | return 103 | } 104 | 105 | //SecretBoxOpenDetached opens a SecretBox using SecretBoxKey and SecretBoxNonce. 106 | //with a separate MAC. 107 | // 108 | //It returns an error if opening failed. 109 | func (b Bytes) SecretBoxOpenDetached(mac SecretBoxMAC, n SecretBoxNonce, k SecretBoxKey) (m Bytes, err error) { 110 | checkTypedSize(&mac, "mac") 111 | checkTypedSize(&n, "nonce") 112 | checkTypedSize(&k, "key") 113 | 114 | bp, bl := plen(b) 115 | m = make([]byte, bl) 116 | mp, _ := plen(m) 117 | if int(C.crypto_secretbox_open_detached( 118 | (*C.uchar)(mp), 119 | (*C.uchar)(bp), 120 | (*C.uchar)(&mac.Bytes[0]), 121 | (C.ulonglong)(bl), 122 | (*C.uchar)(&n.Bytes[0]), 123 | (*C.uchar)(&k.Bytes[0]))) != 0 { 124 | err = ErrOpenBox 125 | } 126 | 127 | return 128 | } 129 | -------------------------------------------------------------------------------- /secretstream.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "io" 10 | ) 11 | 12 | var ( 13 | cryptoSecretStreamXChaCha20Poly1305KeyBytes = int(C.crypto_secretstream_xchacha20poly1305_keybytes()) 14 | cryptoSecretStreamXChaCha20Poly1305HeaderBytes = int(C.crypto_secretstream_xchacha20poly1305_headerbytes()) 15 | ) 16 | 17 | // SecretStreamTag can be set to encoder for modify stream state or can be get from decoder 18 | type SecretStreamTag uint8 19 | 20 | const ( 21 | // normal message chunk 22 | SecretStreamTag_Message = iota 23 | // message boundary, the last chunk of message 24 | SecretStreamTag_Sync 25 | // explicity rekeying 26 | SecretStreamTag_Rekey 27 | ) 28 | 29 | func (tag *SecretStreamTag) fromCtag(ctag C.uchar) { 30 | switch ctag { 31 | case C.crypto_secretstream_xchacha20poly1305_tag_push(): 32 | *tag = SecretStreamTag_Sync 33 | case C.crypto_secretstream_xchacha20poly1305_tag_rekey(): 34 | *tag = SecretStreamTag_Rekey 35 | default: 36 | *tag = SecretStreamTag_Message 37 | } 38 | return 39 | } 40 | 41 | func (tag SecretStreamTag) toCtag() (ctag C.uchar) { 42 | switch tag { 43 | case SecretStreamTag_Sync: 44 | ctag = C.crypto_secretstream_xchacha20poly1305_tag_push() 45 | case SecretStreamTag_Rekey: 46 | ctag = C.crypto_secretstream_xchacha20poly1305_tag_rekey() 47 | default: 48 | ctag = C.crypto_secretstream_xchacha20poly1305_tag_message() 49 | } 50 | return 51 | } 52 | 53 | type SecretStreamXCPKey struct { 54 | Bytes 55 | } 56 | 57 | func (SecretStreamXCPKey) Size() int { 58 | return cryptoSecretStreamXChaCha20Poly1305KeyBytes 59 | } 60 | 61 | // MakeSecretStreamXCPKey initilize the key 62 | func MakeSecretStreamXCPKey() SecretStreamXCPKey { 63 | b := make([]byte, cryptoSecretStreamXChaCha20Poly1305KeyBytes); 64 | C.crypto_secretstream_xchacha20poly1305_keygen((*C.uchar)(&b[0])) 65 | return SecretStreamXCPKey{b} 66 | } 67 | 68 | // SecretStreamXCPHeader generated by encoder and can be transferred in plain text. It must set to decoder before decoding 69 | type SecretStreamXCPHeader struct { 70 | Bytes 71 | } 72 | 73 | func (SecretStreamXCPHeader) Size() int { 74 | return cryptoSecretStreamXChaCha20Poly1305HeaderBytes 75 | } 76 | 77 | type SecretStreamEncoder interface { 78 | io.WriteCloser 79 | Header() SecretStreamXCPHeader 80 | SetAdditionData(ad []byte) 81 | SetTag(SecretStreamTag) 82 | WriteAndClose(b []byte) (n int, err error) 83 | } 84 | 85 | type SecretStreamDecoder interface { 86 | io.Reader 87 | SetAdditionData(ad []byte) 88 | Tag() SecretStreamTag 89 | } 90 | 91 | type SecretStreamXCPEncoder struct { 92 | out io.Writer 93 | header SecretStreamXCPHeader 94 | state C.crypto_secretstream_xchacha20poly1305_state 95 | ad Bytes 96 | tag SecretStreamTag 97 | final bool 98 | } 99 | 100 | type SecretStreamXCPDecoder struct { 101 | in io.Reader 102 | state C.crypto_secretstream_xchacha20poly1305_state 103 | ad Bytes 104 | tag SecretStreamTag 105 | final bool 106 | } 107 | 108 | // Header get the header from encoder 109 | func (e SecretStreamXCPEncoder) Header() SecretStreamXCPHeader { 110 | return e.header 111 | } 112 | 113 | func (e *SecretStreamXCPEncoder) SetAdditionData(ad []byte) { 114 | e.ad = ad[:] 115 | } 116 | 117 | func (e *SecretStreamXCPEncoder) SetTag(t SecretStreamTag) { 118 | e.tag = t 119 | } 120 | 121 | // Write encrypts the b as a message and write to the wrapped io.Writer 122 | func (e *SecretStreamXCPEncoder) Write(b []byte) (n int, err error) { 123 | if e.final { 124 | return n, ErrInvalidState 125 | } 126 | mp, ml := plen(b) 127 | c := make([]byte, ml + int(C.crypto_secretstream_xchacha20poly1305_abytes())) 128 | cp, _ := plen(c) 129 | adp, adl := plen(e.ad) 130 | if int(C.crypto_secretstream_xchacha20poly1305_push(&e.state, 131 | (*C.uchar)(cp), 132 | (*C.ulonglong)(nil), 133 | (*C.uchar)(mp), 134 | (C.ulonglong)(ml), 135 | (*C.uchar)(adp), 136 | (C.ulonglong)(adl), 137 | e.tag.toCtag())) != 0 { 138 | return 0, ErrUnknown 139 | } 140 | n, err = e.out.Write(c) 141 | return 142 | } 143 | 144 | // Write encrypts the b as a message and write to the wrapped io.Writer and then write the closing signal 145 | func (e *SecretStreamXCPEncoder) WriteAndClose(b []byte) (n int, err error) { 146 | if e.final { 147 | return n, ErrInvalidState 148 | } 149 | mp, ml := plen(b) 150 | c := make([]byte, ml + int(C.crypto_secretstream_xchacha20poly1305_abytes())) 151 | cp, _ := plen(c) 152 | adp, adl := plen(e.ad) 153 | if int(C.crypto_secretstream_xchacha20poly1305_push(&e.state, 154 | (*C.uchar)(cp), 155 | (*C.ulonglong)(nil), 156 | (*C.uchar)(mp), 157 | (C.ulonglong)(ml), 158 | (*C.uchar)(adp), 159 | (C.ulonglong)(adl), 160 | C.crypto_secretstream_xchacha20poly1305_tag_final())) != 0 { 161 | return 0, ErrUnknown 162 | } 163 | n, err = e.out.Write(c) 164 | e.final = true 165 | return 166 | } 167 | 168 | // Write encrypts the closing signal and write to the wrapped io.Writer and then close it 169 | func (e *SecretStreamXCPEncoder) Close() error { 170 | mac := make([]byte, int(C.crypto_secretstream_xchacha20poly1305_abytes())) 171 | ap, _ := plen(mac) 172 | adp, adl := plen(e.ad) 173 | if int(C.crypto_secretstream_xchacha20poly1305_push(&e.state, 174 | (*C.uchar)(ap), 175 | (*C.ulonglong)(nil), 176 | (*C.uchar)(nil), 177 | 0, 178 | (*C.uchar)(adp), 179 | (C.ulonglong)(adl), 180 | C.crypto_secretstream_xchacha20poly1305_tag_final())) != 0 { 181 | return ErrUnknown 182 | } 183 | _, err := e.out.Write(mac) 184 | e.final = true 185 | return err 186 | } 187 | 188 | func MakeSecretStreamXCPEncoder (key SecretStreamXCPKey, out io.Writer) SecretStreamEncoder { 189 | checkTypedSize(&key, "secret stream key") 190 | encoder := SecretStreamXCPEncoder{ 191 | out: out, 192 | header: SecretStreamXCPHeader{make([]byte, cryptoSecretStreamXChaCha20Poly1305HeaderBytes)}, 193 | } 194 | if int(C.crypto_secretstream_xchacha20poly1305_init_push( 195 | &encoder.state, 196 | (*C.uchar)(&encoder.header.Bytes[0]), 197 | (*C.uchar)(&key.Bytes[0]))) != 0 { 198 | panic("see libsodium") 199 | } 200 | return &encoder 201 | } 202 | 203 | // Read decrypts the message with length len(b) and save in b. It returns io.EOF when receiving a closing signal 204 | func (e *SecretStreamXCPDecoder) Read(b []byte) (n int, err error) { 205 | if e.final { 206 | return n, ErrInvalidState 207 | } 208 | bp, bl := plen(b) 209 | c := make([]byte, bl + int(C.crypto_secretstream_xchacha20poly1305_abytes())) 210 | 211 | var l int 212 | l, err = e.in.Read(c) 213 | for l < int(C.crypto_secretstream_xchacha20poly1305_abytes()) { 214 | if err != nil { 215 | return 0, ErrDecryptSS 216 | } 217 | var more int 218 | more, err = e.in.Read(c[:l]) 219 | l += more 220 | } 221 | adp, adl := plen(e.ad) 222 | var tag C.uchar 223 | if int(C.crypto_secretstream_xchacha20poly1305_pull( 224 | &e.state, 225 | (*C.uchar)(bp), 226 | (*C.ulonglong)(nil), 227 | &tag, 228 | (*C.uchar)(&c[0]), 229 | (C.ulonglong)(l), 230 | (*C.uchar)(adp), 231 | (C.ulonglong)(adl))) != 0 { 232 | err = ErrDecryptSS 233 | } 234 | n = l - int(C.crypto_secretstream_xchacha20poly1305_abytes()) 235 | e.tag.fromCtag(tag) 236 | if tag == C.crypto_secretstream_xchacha20poly1305_tag_final() { 237 | err = io.EOF 238 | e.final = true 239 | } 240 | return 241 | } 242 | 243 | func (e *SecretStreamXCPDecoder) SetAdditionData(ad []byte) { 244 | e.ad = ad[:] 245 | } 246 | 247 | func (e SecretStreamXCPDecoder) Tag() SecretStreamTag { 248 | return e.tag 249 | } 250 | 251 | func MakeSecretStreamXCPDecoder (key SecretStreamXCPKey, in io.Reader, header SecretStreamXCPHeader) (SecretStreamDecoder, error) { 252 | checkTypedSize(&key, "secret stream key") 253 | checkTypedSize(&header, "secret stream header") 254 | decoder := SecretStreamXCPDecoder{ 255 | in: in, 256 | } 257 | if int(C.crypto_secretstream_xchacha20poly1305_init_pull( 258 | &decoder.state, 259 | (*C.uchar)(&header.Bytes[0]), 260 | (*C.uchar)(&key.Bytes[0]))) != 0 { 261 | return nil, ErrInvalidHeader 262 | } 263 | return &decoder, nil 264 | } 265 | -------------------------------------------------------------------------------- /shorthash.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | cryptoShortHashBytes = int(C.crypto_shorthash_bytes()) 10 | cryptoShortHashKeyBytes = int(C.crypto_shorthash_keybytes()) 11 | ) 12 | 13 | type ShortHash struct { 14 | Bytes 15 | } 16 | 17 | func (s ShortHash) Size() int { 18 | return cryptoShortHashBytes 19 | } 20 | 21 | type ShortHashKey struct { 22 | Bytes 23 | } 24 | 25 | func (s ShortHashKey) Size() int { 26 | return cryptoShortHashKeyBytes 27 | } 28 | 29 | //Shorthash use a secret key and input to produce a ShortHash. 30 | //It is protective to short input. And it's output is also too short to 31 | //be collision-resistent, however it can be used in hash table, Bloom filter 32 | //or generate MAC for interactive protocol. 33 | func (b Bytes) Shorthash(key ShortHashKey) (out Bytes) { 34 | checkTypedSize(&key, "key") 35 | 36 | bp, bl := plen(b) 37 | 38 | out = make([]byte, cryptoShortHashBytes) 39 | if int(C.crypto_shorthash( 40 | (*C.uchar)(&out[0]), 41 | (*C.uchar)(bp), 42 | (C.ulonglong)(bl), 43 | (*C.uchar)(&key.Bytes[0]))) != 0 { 44 | panic("see libsodium") 45 | } 46 | 47 | return 48 | } 49 | -------------------------------------------------------------------------------- /sign.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | // #cgo pkg-config: libsodium 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | var ( 9 | cryptoSignBytes = int(C.crypto_sign_bytes()) 10 | cryptoSignSeedBytes = int(C.crypto_sign_seedbytes()) 11 | cryptoSignPublicKeyBytes = int(C.crypto_sign_publickeybytes()) 12 | cryptoSignSecretKeyBytes = int(C.crypto_sign_secretkeybytes()) 13 | cryptoSignPrimitive = C.GoString(C.crypto_sign_primitive()) 14 | ) 15 | 16 | type SignKP struct { 17 | PublicKey SignPublicKey 18 | SecretKey SignSecretKey 19 | } 20 | 21 | //MakeSignKP generates a keypair for signing 22 | func MakeSignKP() SignKP { 23 | pkb := make([]byte, cryptoSignPublicKeyBytes) 24 | skb := make([]byte, cryptoSignSecretKeyBytes) 25 | if int(C.crypto_sign_keypair( 26 | (*C.uchar)(&pkb[0]), 27 | (*C.uchar)(&skb[0]))) != 0 { 28 | panic("see libsodium") 29 | } 30 | 31 | return SignKP{ 32 | SignPublicKey{pkb}, 33 | SignSecretKey{skb}, 34 | } 35 | } 36 | 37 | //SeedSignKP generates a keypair for signing from a SignSeed. 38 | // 39 | //The same pair of keys will be generated with the same 'seed' 40 | func SeedSignKP(seed SignSeed) SignKP { 41 | checkTypedSize(&seed, "seed") 42 | pkb := make([]byte, cryptoSignPublicKeyBytes) 43 | skb := make([]byte, cryptoSignSecretKeyBytes) 44 | if int(C.crypto_sign_seed_keypair( 45 | (*C.uchar)(&pkb[0]), 46 | (*C.uchar)(&skb[0]), 47 | (*C.uchar)(&seed.Bytes[0]))) != 0 { 48 | panic("see libsodium") 49 | } 50 | 51 | return SignKP{ 52 | SignPublicKey{pkb}, 53 | SignSecretKey{skb}, 54 | } 55 | } 56 | 57 | //ToBox converts a signing secret key into a box secret key - ed25519 to curve25519 - returns BoxSecretKey. 58 | func (k SignSecretKey) ToBox() BoxSecretKey { 59 | checkTypedSize(&k, "Sign SecretKey") 60 | skb := make([]byte, cryptoBoxSecretKeyBytes) 61 | C.crypto_sign_ed25519_sk_to_curve25519( 62 | (*C.uchar)(&skb[0]), 63 | (*C.uchar)(&k.Bytes[0])) 64 | return BoxSecretKey{skb} 65 | } 66 | 67 | //ToBox converts a signing public key into a box public key - ed25519 to curve25519 - returns BoxPublicKey. 68 | func (k SignPublicKey) ToBox() BoxPublicKey { 69 | checkTypedSize(&k, "Sign PublicKey") 70 | pkb := make([]byte, cryptoBoxPublicKeyBytes) 71 | C.crypto_sign_ed25519_pk_to_curve25519( 72 | (*C.uchar)(&pkb[0]), 73 | (*C.uchar)(&k.Bytes[0])) 74 | return BoxPublicKey{pkb} 75 | } 76 | 77 | //ToBox converts a pair of signing key into a pair of box key - ed25519 to curve25519 - returns BoxKP. 78 | func (p SignKP) ToBox() BoxKP { 79 | return BoxKP{ 80 | p.PublicKey.ToBox(), 81 | p.SecretKey.ToBox(), 82 | } 83 | } 84 | 85 | type SignSeed struct { 86 | Bytes 87 | } 88 | 89 | func (k SignSeed) Size() int { 90 | return cryptoSignSeedBytes 91 | } 92 | 93 | type SignSecretKey struct { 94 | Bytes 95 | } 96 | 97 | func (k SignSecretKey) Size() int { 98 | return cryptoSignSecretKeyBytes 99 | } 100 | 101 | //Seed extracts the seed used when generating the key pair. 102 | func (k SignSecretKey) Seed() SignSeed { 103 | checkTypedSize(&k, "Sign SecretKey") 104 | sb := make([]byte, cryptoSignSeedBytes) 105 | C.crypto_sign_ed25519_sk_to_seed( 106 | (*C.uchar)(&sb[0]), 107 | (*C.uchar)(&k.Bytes[0])) 108 | return SignSeed{sb} 109 | } 110 | 111 | //PublicKey extracts the SignPublicKey from the SignSecretKey. 112 | func (k SignSecretKey) PublicKey() SignPublicKey { 113 | checkTypedSize(&k, "Sign SecretKey") 114 | pkb := make([]byte, cryptoSignPublicKeyBytes) 115 | C.crypto_sign_ed25519_sk_to_pk( 116 | (*C.uchar)(&pkb[0]), 117 | (*C.uchar)(&k.Bytes[0])) 118 | return SignPublicKey{pkb} 119 | } 120 | 121 | type SignPublicKey struct { 122 | Bytes 123 | } 124 | 125 | func (k SignPublicKey) Size() int { 126 | return cryptoSignPublicKeyBytes 127 | } 128 | 129 | type Signature struct { 130 | Bytes 131 | } 132 | 133 | func (b Signature) Size() int { 134 | return cryptoSignBytes 135 | } 136 | 137 | //Sign returns 'sm': signature+message 138 | func (b Bytes) Sign(key SignSecretKey) (sm Bytes) { 139 | checkTypedSize(&key, "Sign SecretKey") 140 | bp, bl := plen(b) 141 | sm = make([]byte, bl+cryptoSignBytes) 142 | var smlen C.ulonglong 143 | 144 | if int(C.crypto_sign( 145 | (*C.uchar)(&sm[0]), 146 | &smlen, 147 | (*C.uchar)(bp), 148 | (C.ulonglong)(bl), 149 | (*C.uchar)(&key.Bytes[0]))) != 0 { 150 | panic("see libsodium") 151 | } 152 | sm = sm[:smlen] 153 | 154 | return 155 | } 156 | 157 | //SignDetached signs the message with 'key' and returns only the signature. 158 | func (b Bytes) SignDetached(key SignSecretKey) (sig Signature) { 159 | checkTypedSize(&key, "Sign SecretKey") 160 | sigb := make([]byte, cryptoSignBytes) 161 | bp, bl := plen(b) 162 | var siglen C.ulonglong 163 | 164 | if int(C.crypto_sign_detached( 165 | (*C.uchar)(&sigb[0]), 166 | &siglen, 167 | (*C.uchar)(bp), 168 | (C.ulonglong)(bl), 169 | (*C.uchar)(&key.Bytes[0]))) != 0 { 170 | panic("see libsodium") 171 | } 172 | sig = Signature{sigb[:siglen]} 173 | 174 | return 175 | } 176 | 177 | //SignVerifyDetached verifies the message and its detached 'sig' with 'key'. 178 | // 179 | //It returns an error if verification failed. 180 | func (b Bytes) SignVerifyDetached(sig Signature, key SignPublicKey) (err error) { 181 | checkTypedSize(&sig, "Signature") 182 | checkTypedSize(&key, "Sign PublicKey") 183 | bp, bl := plen(b) 184 | if int(C.crypto_sign_verify_detached( 185 | (*C.uchar)(&sig.Bytes[0]), 186 | (*C.uchar)(bp), 187 | (C.ulonglong)(bl), 188 | (*C.uchar)(&key.Bytes[0]))) != 0 { 189 | err = ErrOpenSign 190 | } 191 | return 192 | } 193 | 194 | //SignOpen returns message 'm' from signature+message, verified by 'key'. 195 | // 196 | //It returns an error if verification failed. 197 | func (b Bytes) SignOpen(key SignPublicKey) (m Bytes, err error) { 198 | checkTypedSize(&key, "Sign PublicKey") 199 | bp, bl := plen(b) 200 | m = make([]byte, bl-cryptoSignBytes) 201 | mp, _ := plen(m) 202 | var mlen C.ulonglong 203 | 204 | if int(C.crypto_sign_open( 205 | (*C.uchar)(mp), 206 | &mlen, 207 | (*C.uchar)(bp), 208 | (C.ulonglong)(bl), 209 | (*C.uchar)(&key.Bytes[0]))) != 0 { 210 | err = ErrOpenSign 211 | } 212 | m = m[:mlen] 213 | return 214 | } 215 | 216 | type SignState struct { 217 | state C.struct_crypto_sign_ed25519ph_state 218 | } 219 | 220 | // NewSignState creates an empty state for multi-part messages that can't fit 221 | // in memory. 222 | func NewSignState() *SignState { 223 | s := SignState{} 224 | if int(C.crypto_sign_init( 225 | &s.state)) != 0 { 226 | panic("see libsodium") 227 | } 228 | return &s 229 | } 230 | 231 | // Update the state by add more data. 232 | func (s *SignState) Update(b []byte) { 233 | bp, bl := plen(b) 234 | if int(C.crypto_sign_update( 235 | &s.state, 236 | (*C.uchar)(bp), 237 | (C.ulonglong)(bl))) != 0 { 238 | panic("see libsodium") 239 | } 240 | return 241 | } 242 | 243 | // Sign a signature for the current state. 244 | // 245 | // The underlying state is freed after this call. 246 | func (s *SignState) Sign(key SignSecretKey) Signature { 247 | checkTypedSize(&key, "Sign SecretKey") 248 | sigb := make([]byte, cryptoSignBytes) 249 | var siglen C.ulonglong 250 | 251 | if int(C.crypto_sign_final_create( 252 | &s.state, 253 | (*C.uchar)(&sigb[0]), 254 | &siglen, 255 | (*C.uchar)(&key.Bytes[0]))) != 0 { 256 | panic("see libsodium") 257 | } 258 | s.state = C.struct_crypto_sign_ed25519ph_state{} 259 | 260 | return Signature{sigb[:siglen]} 261 | } 262 | 263 | // Verify the signature with the current state and public key. 264 | // 265 | // It returns an error if verification failed. 266 | func (s *SignState) Verify(sig Signature, key SignPublicKey) (err error) { 267 | checkTypedSize(&sig, "Signature") 268 | checkTypedSize(&key, "Sign PublicKey") 269 | if int(C.crypto_sign_final_verify( 270 | &s.state, 271 | (*C.uchar)(&sig.Bytes[0]), 272 | (*C.uchar)(&key.Bytes[0]))) != 0 { 273 | err = ErrOpenSign 274 | } 275 | return 276 | } 277 | -------------------------------------------------------------------------------- /sodium.go: -------------------------------------------------------------------------------- 1 | //Package sodium is a wrapper for https://github.com/jedisct1/libsodium 2 | // 3 | //Most of the functions is a method to the "Bytes" type. 4 | //They are grouped below: 5 | // 6 | //Signature 7 | // 8 | //Sender sign a message with its SecretKey and the receiver can verify the 9 | //Signature by sender's PublicKey 10 | // 11 | // type SignKP struct { 12 | // PublicKey SignPublicKey 13 | // SecretKey SignSecretKey 14 | // } 15 | // func MakeSignKP() SignKP 16 | // func SeedSignKP(seed SignSeed) SignKP 17 | // func (k SignSecretKey) PublicKey() SignPublicKey 18 | // func (k SignSecretKey) Seed() SignSeed 19 | // 20 | // //SignKP can be converted to BoxKP 21 | // //It is recommended to use separate keys for signing and encrytion. 22 | // func (p SignKP) ToBox() BoxKP 23 | // func (k SignSecretKey) ToBox() BoxSecretKey 24 | // func (k SignPublicKey) ToBox() BoxPublicKey 25 | // 26 | // //Message + Signature 27 | // func (b Bytes) Sign(key SignSecretKey) (sm Bytes) 28 | // func (b Bytes) SignOpen(key SignPublicKey) (m Bytes, err error) 29 | // 30 | // //Detached Signature 31 | // func (b Bytes) SignDetached(key SignSecretKey) (sig Signature) 32 | // func (b Bytes) SignVerifyDetached(sig Signature, key SignPublicKey) (err error) 33 | // 34 | //(Ed25519) 35 | // 36 | // //for multi-part messages that can't fit in memory 37 | // func NewSignState() *SignState 38 | // func (s *SignState) Update(b []byte) 39 | // func (s *SignState) Sign(key SignSecretKey) Signature 40 | // func (s *SignState) Verify(sig Signature, key SignPublicKey) (err error) 41 | // 42 | //(Ed25519ph) 43 | // 44 | //Anonymous Public Key Encryption 45 | // 46 | //An anonymous can encrypt a message with an ephemeral key pair and reveiver's 47 | //PublicKey. The receiver can decrypt the message with its SecretKey. Only the 48 | //receiver is authenticated. 49 | // 50 | // 51 | // type BoxKP struct { 52 | // PublicKey BoxPublicKey 53 | // SecretKey BoxSecretKey 54 | // } 55 | // func MakeBoxKP() BoxKP 56 | // func SeedBoxKP(seed BoxSeed) BoxKP 57 | // 58 | // func (b Bytes) SealedBox(pk BoxPublicKey) (cm Bytes) 59 | // func (b Bytes) SealedBoxOpen(kp BoxKP) (m Bytes, err error) 60 | // 61 | //(X25519-XSalsa20-Poly1305) 62 | // 63 | //Authenticated Public Key Encryption 64 | // 65 | //Authenticated Box can be used to pass encrypt message from a known sender to a known receiver. 66 | //The sender and the receiver are both authenticated to each other. 67 | // 68 | //A one-time shared nonce is also generated and passed to protect the key pairs and messages. 69 | // 70 | // type BoxKP struct { 71 | // PublicKey BoxPublicKey 72 | // SecretKey BoxSecretKey 73 | // } 74 | // func MakeBoxKP() BoxKP 75 | // func SeedBoxKP(seed BoxSeed) BoxKP 76 | // 77 | // func (b *BoxNonce) Next() 78 | // 79 | // //All-in-one box 80 | // func (b Bytes) Box(n BoxNonce, pk BoxPublicKey, sk BoxSecretKey) (c Bytes) 81 | // func (b Bytes) BoxOpen(n BoxNonce, pk BoxPublicKey, sk BoxSecretKey) (m Bytes, err error) 82 | // 83 | // //Detached MAC 84 | // func (b Bytes) BoxDetached(n BoxNonce, pk BoxPublicKey, sk BoxSecretKey) (mac BoxMAC, c Bytes) 85 | // func (b Bytes) BoxOpenDetached(mac BoxMAC, n BoxNonce, pk BoxPublicKey, sk BoxSecretKey) (c Bytes, err error) 86 | // 87 | //(X25519-XSalsa20-Poly1305) 88 | // 89 | //Key Exchanging 90 | // 91 | //Server and Client exchange their public key and calculates a common session key with their own 92 | //secret key. 93 | // 94 | // type KXKP struct { 95 | // PublicKey KXPublicKey 96 | // SecretKey KXSecretKey 97 | // } 98 | // func MakeKXKP() KXKP 99 | // func SeedKXKP(seed KXSeed) KXKP 100 | // 101 | // type KXSessionKeys struct { 102 | // Rx KXSessionKey 103 | // Tx KXSessionKey 104 | // } 105 | // 106 | // // session keys for client 107 | // func (kp KXKP) ClientSessionKeys(server_pk KXPublicKey) (*KXSessionKeys, error) 108 | // 109 | // // session keys for server 110 | // func (kp KXKP) ServerSessionKeys(client_pk KXPublicKey) (*KXSessionKeys, error) { 111 | // // client's rx == server's tx 112 | // // client's tx == server's rx 113 | // 114 | //(rx || tx = BLAKE2B-512(p.n || client_pk || server_pk)) 115 | // 116 | //Secret Key Authentication 117 | // 118 | //One holder of a secret key authenticates the message with MAC. 119 | // 120 | // //Holders of the key can generate a MAC for the message. 121 | // func (b Bytes) Auth(key MACKey) (mac MAC) 122 | // //Holders of the key can verify the message's authenticity. 123 | // func (b Bytes) AuthVerify(mac MAC, key MACKey) (err error) 124 | // 125 | //(HMAC-SHA512256) 126 | // 127 | //Secret Key Encryption 128 | // 129 | //Use a secret key and a nonce to protect the key, messages could be encrypted 130 | //into a SecretBox. The encrypted data's intergrity is checked when decryption. 131 | // 132 | // func (n *SecretBoxNonce) Next() 133 | // 134 | // //encrypted message + MAC. 135 | // func (b Bytes) SecretBox(n SecretBoxNonce, k SecretBoxKey) (c Bytes) 136 | // func (b Bytes) SecretBoxOpen(n SecretBoxNonce, k SecretBoxKey) (m Bytes, err error) 137 | // 138 | // //Detached version has a separate MAC. 139 | // func (b Bytes) SecretBoxDetached(n SecretBoxNonce, k SecretBoxKey) (c Bytes, mac SecretBoxMAC) 140 | // func (b Bytes) SecretBoxOpenDetached(mac SecretBoxMAC, n SecretBoxNonce, k SecretBoxKey) (m Bytes, err error) 141 | // 142 | //(XSalsa20-Poly1305) 143 | // 144 | //Authenticated Encryption with Additional Data 145 | // 146 | //Use a secret key and a nonce to protect the key, messages could be encrypted. 147 | //Optional additional data and the message is authenticited with an 148 | //authentication tag. Both intergrity and authenticity is checked when 149 | //decryption. The decryption would not be performed unless the authentication 150 | //tag is verified. 151 | // 152 | // func MakeAEADCPKey() AEADCPKey 153 | // func (n *AEADCPNonce) Next() 154 | // 155 | // //encrypted message + MAC. 156 | // func (b Bytes) AEADCPEncrypt(ad Bytes, n AEADCPNonce, k AEADCPKey) (c Bytes) 157 | // func (b Bytes) AEADCPDecrypt(ad Bytes, n AEADCPNonce, k AEADCPKey) (m Bytes, err error) 158 | // func (b Bytes) AEADCPVerify(ad Bytes, n AEADCPNonce, k AEADCPKey) (err error) 159 | // 160 | // //Detached version has a separate MAC. 161 | // func (b Bytes) AEADCPEncryptDetached(ad Bytes, n AEADCPNonce, k AEADCPKey) (c Bytes, mac AEADCPMAC) 162 | // func (b Bytes) AEADCPDecryptDetached(mac AEADCPMAC, ad Bytes, n AEADCPNonce, k AEADCPKey) (m Bytes, err error) 163 | // func (b Bytes) AEADCPVerifyDetached(mac AEADCPMAC, ad Bytes, n AEADCPNonce, k AEADCPKey) (err error) 164 | // 165 | //AEADCP* (ChaCha20-Poly1305_IETF) 166 | //AEADXCP* (XChaCha20-Poly1305_IETF) 167 | // 168 | //Secret Key Streaming Encryption 169 | // 170 | //High-level streaming API that use AEAD construct. Using 171 | //`SecretStreamTag_Sync` to indicate the end of message. And this is 172 | //useful for application to parse the message earlier. Rekeying is 173 | //automatic. However using `SecretStreamTag_Rekey` explicitly ask for 174 | //rekeying. Typical usage is sending chunks with 175 | //`SecretStreamTag_Message`. 176 | // 177 | // func MakeSecretStreamXCPKey() SecretStreamXCPKey 178 | // 179 | // //decoder 180 | // func MakeSecretStreamXCPDecoder(key SecretStreamXCPKey, in io.Reader, header SecretStreamXCPHeader) (SecretStreamDecoder, error) 181 | // func (e *SecretStreamXCPDecoder) Read(b []byte) (n int, err error) 182 | // func (e *SecretStreamXCPDecoder) SetAdditionData(ad []byte) 183 | // func (e SecretStreamXCPDecoder) Tag() SecretStreamTag 184 | // 185 | // //encoder 186 | // func MakeSecretStreamXCPEncoder(key SecretStreamXCPKey, out io.Writer) SecretStreamEncoder 187 | // func (e *SecretStreamXCPEncoder) Close() error 188 | // func (e SecretStreamXCPEncoder) Header() SecretStreamXCPHeader 189 | // func (e *SecretStreamXCPEncoder) SetAdditionData(ad []byte) 190 | // func (e *SecretStreamXCPEncoder) SetTag(t SecretStreamTag) 191 | // func (e *SecretStreamXCPEncoder) Write(b []byte) (n int, err error) 192 | // func (e *SecretStreamXCPEncoder) WriteAndClose(b []byte) (n int, err error) 193 | // 194 | //XCP (XChaCha20-Poly1305_IETF) 195 | // 196 | //Key Derivation 197 | // 198 | //Deriving subkeys from a single high-entropy key 199 | // 200 | // func MakeMasterKey() MasterKey 201 | // func MakeKeyContext(s string) KeyContext 202 | // func (m MasterKey) Derive(length int, id uint64, context KeyContext) SubKey 203 | //KDF (BLAKE2B) 204 | package sodium 205 | 206 | import ( 207 | "crypto/rand" 208 | "errors" 209 | "fmt" 210 | "unsafe" 211 | ) 212 | 213 | var ( 214 | ErrAuth = errors.New("sodium: Message forged") 215 | ErrOpenBox = errors.New("sodium: Can't open box") 216 | ErrOpenSign = errors.New("sodium: Signature forged") 217 | ErrDecryptAEAD = errors.New("sodium: Can't decrypt message") 218 | ErrPassword = errors.New("sodium: Password not matched") 219 | ErrInvalidKey = errors.New("sodium: Invalid key") 220 | ErrInvalidHeader = errors.New("sodium: Invalid header") 221 | ErrDecryptSS = errors.New("sodium: Can't decrypt stream") 222 | ErrInvalidState = errors.New("sodium: Invalid state") 223 | ErrUnknown = errors.New("sodium: Unknown") 224 | ) 225 | 226 | //Typed has pre-defined size. 227 | type Typed interface { 228 | Size() int // Size returns the pre-defined size of the object. 229 | Length() int 230 | setBytes(Bytes) 231 | } 232 | 233 | //Bytes warppers around []byte. 234 | type Bytes []byte 235 | 236 | //Length returns the byte length. 237 | func (b Bytes) Length() int { 238 | return len(b) 239 | } 240 | 241 | func (b *Bytes) setBytes(s Bytes) { 242 | *b = s[:] 243 | } 244 | 245 | func plen(b []byte) (unsafe.Pointer, int) { 246 | if len(b) > 0 { 247 | return unsafe.Pointer(&b[0]), len(b) 248 | } else { 249 | return nil, 0 250 | } 251 | } 252 | 253 | //Nonce is used to protect secret key. It is important to not use the same nonce for a given key. 254 | type Nonce interface { 255 | Typed 256 | Next() //Next unused nonce 257 | } 258 | 259 | //Randomize fill the Typed with random bytes. 260 | func Randomize(k Typed) { 261 | b := make([]byte, k.Size()) 262 | if _, err := rand.Read(b); err != nil { 263 | fmt.Println("error:", err) 264 | return 265 | } 266 | k.setBytes(b) 267 | } 268 | -------------------------------------------------------------------------------- /sodium_test.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "fmt" 7 | "io" 8 | "testing" 9 | ) 10 | 11 | var m = Bytes(make([]byte, 1024)) 12 | 13 | func TestInit(t *testing.T) { 14 | rand.Read(m) 15 | } 16 | 17 | func TestByte(t *testing.T) { 18 | b := Bytes{} 19 | bp, bl := plen(b) 20 | if bp != nil { 21 | t.FailNow() 22 | } 23 | if bl != 0 { 24 | t.FailNow() 25 | } 26 | 27 | b = Bytes(make([]byte, 0, 1024)) 28 | bp, bl = plen(b) 29 | if bp != nil { 30 | t.FailNow() 31 | } 32 | if bl != 0 { 33 | t.FailNow() 34 | } 35 | 36 | b = Bytes(make([]byte, 1024)) 37 | bp, bl = plen(b) 38 | if bp == nil { 39 | t.FailNow() 40 | } 41 | if bl != 1024 { 42 | t.FailNow() 43 | } 44 | } 45 | 46 | func ExampleBytes_Auth() { 47 | key := MACKey{} 48 | Randomize(&key) 49 | mac := m.Auth(key) 50 | 51 | err := m.AuthVerify(mac, key) 52 | fmt.Println(err) 53 | //Output: 54 | } 55 | 56 | func ExampleSeedSignKP() { 57 | seed := SignSeed{} 58 | Randomize(&seed) 59 | kp1 := SeedSignKP(seed) 60 | kp2 := SeedSignKP(seed) 61 | s1 := kp1.SecretKey 62 | s2 := kp2.SecretKey 63 | 64 | s := s1.Seed() 65 | pk1 := s1.PublicKey() 66 | 67 | fmt.Println(MemCmp(s1.Bytes, s2.Bytes, s1.Length()) == 0) 68 | fmt.Println(MemCmp(s.Bytes, seed.Bytes, seed.Length()) == 0) 69 | fmt.Println(MemCmp(pk1.Bytes, kp1.PublicKey.Bytes, pk1.Length()) == 0) 70 | //Output: true 71 | //true 72 | //true 73 | } 74 | 75 | func ExampleBytes_SignDetached() { 76 | kp := MakeSignKP() 77 | sm := m.Sign(kp.SecretKey) 78 | 79 | om, err := sm.SignOpen(kp.PublicKey) 80 | sig := m.SignDetached(kp.SecretKey) 81 | 82 | fmt.Println(err) 83 | fmt.Println(MemCmp(om, m, m.Length()) == 0) 84 | fmt.Println(MemCmp(sig.Bytes, sm, sig.Length()) == 0) 85 | 86 | err = m.SignVerifyDetached(sig, kp.PublicKey) 87 | fmt.Println(err) 88 | //Output: 89 | //true 90 | //true 91 | // 92 | } 93 | 94 | func ExampleNewSignState() { 95 | kp := MakeSignKP() 96 | 97 | s_a := NewSignState() 98 | s_a.Update(m) 99 | 100 | siga := s_a.Sign(kp.SecretKey) 101 | 102 | s_b := NewSignState() 103 | s_b.Update(m) 104 | 105 | err := s_b.Verify(siga, kp.PublicKey) 106 | fmt.Println(err) 107 | //Output: 108 | } 109 | 110 | func ExampleSeedBoxKP() { 111 | seed := BoxSeed{} 112 | Randomize(&seed) 113 | kp1 := SeedBoxKP(seed) 114 | kp2 := SeedBoxKP(seed) 115 | s1 := kp1.SecretKey 116 | s2 := kp2.SecretKey 117 | 118 | fmt.Println(MemCmp(s1.Bytes, s2.Bytes, s1.Length()) == 0) 119 | //Output: true 120 | } 121 | 122 | func ExampleBytes_SealedBox() { 123 | kp := MakeBoxKP() 124 | 125 | n := BoxNonce{} 126 | Randomize(&n) 127 | 128 | c := m.SealedBox(kp.PublicKey) 129 | om, err := c.SealedBoxOpen(kp) 130 | 131 | fmt.Println(err) 132 | fmt.Println(MemCmp(om, m, m.Length()) == 0) 133 | //Output: 134 | //true 135 | } 136 | 137 | func ExampleBytes_Box() { 138 | rkp := MakeBoxKP() 139 | skp := MakeBoxKP() 140 | 141 | n := BoxNonce{} 142 | Randomize(&n) 143 | 144 | bc := m.Box(n, rkp.PublicKey, skp.SecretKey) 145 | bom, err := bc.BoxOpen(n, skp.PublicKey, rkp.SecretKey) 146 | 147 | fmt.Println(err) 148 | fmt.Println(MemCmp(bom, m, m.Length()) == 0) 149 | //Output: 150 | //true 151 | } 152 | 153 | func ExampleBytes_BoxDetached() { 154 | rkp := MakeBoxKP() 155 | skp := MakeBoxKP() 156 | 157 | n := BoxNonce{} 158 | Randomize(&n) 159 | 160 | mac, bcd := m.BoxDetached(n, rkp.PublicKey, skp.SecretKey) 161 | bomd, err := bcd.BoxOpenDetached(mac, n, skp.PublicKey, rkp.SecretKey) 162 | 163 | fmt.Println(err) 164 | fmt.Println(MemCmp(bomd, m, m.Length()) == 0) 165 | //Output: 166 | //true 167 | } 168 | 169 | func ExampleBoxSecretKey_PublicKey() { 170 | kp := MakeBoxKP() 171 | pk := kp.SecretKey.PublicKey() 172 | 173 | fmt.Println(MemCmp(kp.PublicKey.Bytes, pk.Bytes, pk.Length()) == 0) 174 | //Output: true 175 | } 176 | 177 | func ExampleNewGenericHashKeyed() { 178 | kp := MakeBoxKP() 179 | key := GenericHashKey{kp.SecretKey.Bytes} 180 | 181 | h := NewGenericHashKeyed(48, key) 182 | h.Write(m) 183 | sh := h.Sum(nil) 184 | 185 | he := NewGenericHash(48) 186 | he.Write(m) 187 | she := he.Sum(nil) 188 | 189 | fmt.Println(len(sh)) 190 | fmt.Println(len(she)) 191 | fmt.Println((MemCmp(sh, she, len(sh))) == 0) 192 | //Output: 48 193 | //48 194 | //false 195 | } 196 | 197 | func ExampleBytes_Shorthash() { 198 | key := ShortHashKey{} 199 | Randomize(&key) 200 | 201 | m := Bytes(`short message`) 202 | hash := m.Shorthash(key) 203 | fmt.Println(hash.Length()) 204 | //Output: 8 205 | } 206 | 207 | func ExampleBytes_SecretBox() { 208 | key := SecretBoxKey{} 209 | Randomize(&key) 210 | n := SecretBoxNonce{} 211 | Randomize(&n) 212 | 213 | c := m.SecretBox(n, key) 214 | md, err := c.SecretBoxOpen(n, key) 215 | fmt.Println(err) 216 | fmt.Println(MemCmp(md, m, m.Length()) == 0) 217 | //Output: 218 | //true 219 | } 220 | 221 | func ExampleBytes_SecretBoxDetached() { 222 | key := SecretBoxKey{} 223 | Randomize(&key) 224 | n := SecretBoxNonce{} 225 | Randomize(&n) 226 | 227 | c, mac := m.SecretBoxDetached(n, key) 228 | md, err := c.SecretBoxOpenDetached(mac, n, key) 229 | fmt.Println(err) 230 | fmt.Println(MemCmp(md, m, m.Length()) == 0) 231 | //Output: 232 | //true 233 | } 234 | 235 | func ExampleBytes_AEADCPEncrypt() { 236 | key := MakeAEADCPKey() 237 | 238 | n := AEADCPNonce{} 239 | Randomize(&n) 240 | 241 | ad := Bytes(`addtional data`) 242 | 243 | e := m.AEADCPEncrypt(ad, n, key) 244 | 245 | md, err := e.AEADCPDecrypt(ad, n, key) 246 | fmt.Println(err) 247 | fmt.Println(MemCmp(md, m, m.Length()) == 0) 248 | //Output: 249 | //true 250 | } 251 | 252 | func ExampleBytes_AEADCPVerify() { 253 | key := MakeAEADCPKey() 254 | 255 | n := AEADCPNonce{} 256 | Randomize(&n) 257 | 258 | ad := Bytes(`addtional data`) 259 | 260 | e := m.AEADCPEncrypt(ad, n, key) 261 | 262 | err := e.AEADCPVerify(ad, n, key) 263 | fmt.Println(err) 264 | //Output: 265 | } 266 | 267 | func ExampleBytes_AEADCPEncryptDetached() { 268 | key := MakeAEADCPKey() 269 | 270 | n := AEADCPNonce{} 271 | Randomize(&n) 272 | 273 | ad := Bytes(`addtional data`) 274 | 275 | e, mac := m.AEADCPEncryptDetached(ad, n, key) 276 | 277 | md, err := e.AEADCPDecryptDetached(mac, ad, n, key) 278 | fmt.Println(err) 279 | fmt.Println(MemCmp(md, m, m.Length()) == 0) 280 | //Output: 281 | //true 282 | } 283 | 284 | func ExampleBytes_AEADCPVerifyDetached() { 285 | key := MakeAEADCPKey() 286 | 287 | n := AEADCPNonce{} 288 | Randomize(&n) 289 | 290 | ad := Bytes(`addtional data`) 291 | 292 | e, mac := m.AEADCPEncryptDetached(ad, n, key) 293 | 294 | err := e.AEADCPVerifyDetached(mac, ad, n, key) 295 | fmt.Println(err) 296 | //Output: 297 | } 298 | 299 | func ExampleBytes_AEADXCPEncrypt() { 300 | key := MakeAEADXCPKey() 301 | 302 | n := AEADXCPNonce{} 303 | Randomize(&n) 304 | 305 | ad := Bytes(`addtional data`) 306 | 307 | e := m.AEADXCPEncrypt(ad, n, key) 308 | 309 | md, err := e.AEADXCPDecrypt(ad, n, key) 310 | fmt.Println(err) 311 | fmt.Println(MemCmp(md, m, m.Length()) == 0) 312 | //Output: 313 | //true 314 | } 315 | 316 | func ExampleBytes_AEADXCPVerify() { 317 | key := MakeAEADXCPKey() 318 | 319 | n := AEADXCPNonce{} 320 | Randomize(&n) 321 | 322 | ad := Bytes(`addtional data`) 323 | 324 | e := m.AEADXCPEncrypt(ad, n, key) 325 | 326 | err := e.AEADXCPVerify(ad, n, key) 327 | fmt.Println(err) 328 | //Output: 329 | } 330 | 331 | func ExampleBytes_AEADXCPEncryptDetached() { 332 | key := MakeAEADXCPKey() 333 | 334 | n := AEADXCPNonce{} 335 | Randomize(&n) 336 | 337 | ad := Bytes(`addtional data`) 338 | 339 | e, mac := m.AEADXCPEncryptDetached(ad, n, key) 340 | 341 | md, err := e.AEADXCPDecryptDetached(mac, ad, n, key) 342 | fmt.Println(err) 343 | fmt.Println(MemCmp(md, m, m.Length()) == 0) 344 | //Output: 345 | //true 346 | } 347 | 348 | func ExampleBytes_AEADXCPVerifyDetached() { 349 | key := MakeAEADXCPKey() 350 | 351 | n := AEADXCPNonce{} 352 | Randomize(&n) 353 | 354 | ad := Bytes(`addtional data`) 355 | 356 | e, mac := m.AEADXCPEncryptDetached(ad, n, key) 357 | 358 | err := e.AEADXCPVerifyDetached(mac, ad, n, key) 359 | fmt.Println(err) 360 | //Output: 361 | } 362 | 363 | func ExamplePWHashStore() { 364 | s := PWHashStore("test") 365 | str := s.Value() // str for store 366 | t := LoadPWHashStr(str) // load from storage 367 | err := t.PWHashVerify("test") 368 | 369 | fmt.Println(err) 370 | //Output: 371 | } 372 | 373 | func ExampleSignKP_ToBox() { 374 | skp := MakeSignKP() 375 | bkp := skp.ToBox() 376 | rkp := MakeBoxKP() 377 | 378 | sb := skp.SecretKey.ToBox() 379 | pb := skp.PublicKey.ToBox() 380 | 381 | fmt.Println(MemCmp(bkp.SecretKey.Bytes, sb.Bytes, bkp.SecretKey.Length()) == 0) 382 | fmt.Println(MemCmp(bkp.PublicKey.Bytes, pb.Bytes, bkp.PublicKey.Length()) == 0) 383 | 384 | n := BoxNonce{} 385 | Randomize(&n) 386 | 387 | bc := m.Box(n, rkp.PublicKey, bkp.SecretKey) 388 | bom, err := bc.BoxOpen(n, bkp.PublicKey, rkp.SecretKey) 389 | 390 | fmt.Println(err) 391 | fmt.Println(MemCmp(bom, m, m.Length()) == 0) 392 | 393 | //Output: true 394 | //true 395 | // 396 | //true 397 | } 398 | 399 | func ExampleMasterKey_Derive() { 400 | mk := MakeMasterKey() 401 | context := MakeKeyContext("testblablabla") // only first CryptoKDFContextBytes is used 402 | fmt.Println(context) 403 | sk := mk.Derive(CryptoKDFBytesMin, 0, context) 404 | 405 | fmt.Println(sk.Length() == CryptoKDFBytesMin) 406 | //Output: testblab 407 | //true 408 | } 409 | 410 | func ExampleMakeKXKP() { 411 | skp := MakeKXKP() 412 | ckp := MakeKXKP() 413 | 414 | sss, _ := skp.ServerSessionKeys(ckp.PublicKey) 415 | css, _ := ckp.ClientSessionKeys(skp.PublicKey) 416 | 417 | fmt.Println(MemCmp(sss.Tx.Bytes, css.Rx.Bytes, sss.Tx.Size()) == 0) 418 | fmt.Println(MemCmp(sss.Rx.Bytes, css.Tx.Bytes, sss.Rx.Size()) == 0) 419 | 420 | fmt.Println(MemCmp(sss.Tx.Bytes, sss.Rx.Bytes, sss.Tx.Size()) == 0) 421 | fmt.Println(MemCmp(css.Tx.Bytes, css.Rx.Bytes, css.Tx.Size()) == 0) 422 | //Output: true 423 | //true 424 | //false 425 | //false 426 | } 427 | 428 | func ExampleSecretStreamXCPEncoder_Header() { 429 | key := MakeSecretStreamXCPKey() 430 | var buf bytes.Buffer 431 | encoder := MakeSecretStreamXCPEncoder(key, &buf) 432 | b := encoder.Header().Bytes 433 | fmt.Println(b.Length()) 434 | //Output: 24 435 | } 436 | 437 | func ExampleSecretStreamXCPEncoder_Write() { 438 | key := MakeSecretStreamXCPKey() 439 | var buf bytes.Buffer 440 | encoder := MakeSecretStreamXCPEncoder(key, &buf) 441 | encoder.SetTag(SecretStreamTag_Sync) 442 | encoder.Write([]byte("test")) 443 | encoder.Close() 444 | fmt.Println(buf.Len()) 445 | //Output: 38 446 | } 447 | 448 | func ExampleSecretStreamXCPEncoder_WriteAndClose() { 449 | key := MakeSecretStreamXCPKey() 450 | var buf bytes.Buffer 451 | encoder := MakeSecretStreamXCPEncoder(key, &buf) 452 | encoder.SetTag(SecretStreamTag_Sync) 453 | encoder.WriteAndClose([]byte("test")) 454 | fmt.Println(buf.Len()) 455 | //Output: 21 456 | } 457 | 458 | func ExampleSecretStreamXCPDecoder_Read() { 459 | key := MakeSecretStreamXCPKey() 460 | var buf bytes.Buffer 461 | encoder := MakeSecretStreamXCPEncoder(key, &buf) 462 | encoder.SetTag(SecretStreamTag_Rekey) 463 | encoder.Write([]byte("test")) 464 | encoder.Close() 465 | 466 | var err error 467 | reader := bytes.NewReader(buf.Bytes()) 468 | decoder, err := MakeSecretStreamXCPDecoder(key, reader, encoder.Header()) 469 | fmt.Println(err == nil) 470 | chunk := make([]byte, 4) 471 | for { 472 | var n int 473 | n, err = decoder.Read(chunk) 474 | fmt.Println(n) 475 | if (n > 0) { 476 | fmt.Println(string(chunk)) 477 | fmt.Println(decoder.Tag() == SecretStreamTag_Rekey) 478 | } else { 479 | fmt.Println(err == io.EOF) 480 | break; 481 | } 482 | } 483 | //Output: true 484 | //4 485 | //test 486 | //true 487 | //0 488 | //true 489 | } 490 | -------------------------------------------------------------------------------- /support.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | import "fmt" 4 | 5 | // 6 | // Internal support functions 7 | // 8 | 9 | // CheckTypedSize verifies the expected size of a Typed byte array. 10 | func checkTypedSize(typed Typed, descrip string) { 11 | switch typed.(type) { 12 | case *GenericHashKey: 13 | got := typed.Length() 14 | min, max := cryptoGenericHashBytesMin, cryptoGenericHashBytesMax 15 | checkSizeInRange(got, min, max, descrip) 16 | case *SubKey: 17 | got := typed.Length() 18 | min, max := CryptoKDFBytesMin, CryptoKDFBytesMax 19 | checkSizeInRange(got, min, max, descrip) 20 | default: 21 | expected := typed.Size() 22 | got := typed.Length() 23 | if got != expected { 24 | panic(fmt.Sprintf("Incorrect %s buffer size, expected (%d), got (%d).\n", descrip, expected, got)) 25 | } 26 | } 27 | } 28 | 29 | func checkSizeInRange(size int, min int, max int, descrip string) { 30 | if size < min || size > max { 31 | panic(fmt.Sprintf("Incorrect %s buffer size, expected (%d - %d), got (%d).", descrip, min, max, size)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package sodium 2 | 3 | import "fmt" 4 | import "unsafe" 5 | 6 | // #cgo pkg-config: libsodium 7 | // #include 8 | import "C" 9 | 10 | //MemZero sets the buffer to zero 11 | func MemZero(buff1 Bytes) { 12 | if len(buff1) > 0 { 13 | C.sodium_memzero(unsafe.Pointer(&buff1[0]), C.size_t(len(buff1))) 14 | } 15 | } 16 | 17 | //MemCmp compare to buffer without leaking timing infomation 18 | func MemCmp(buff1, buff2 Bytes, length int) int { 19 | if length > buff1.Length() || length > buff2.Length() { 20 | panic(fmt.Sprintf("Attempt to compare more bytes (%d) than provided "+ 21 | "(%d, %d)", length, len(buff1), len(buff2))) 22 | } 23 | b1, _ := plen(buff1) 24 | b2, _ := plen(buff2) 25 | return int(C.sodium_memcmp(b1, b2, C.size_t(length))) 26 | } 27 | --------------------------------------------------------------------------------