func s2v(key []byte, strings [][]byte) []byte { if len(key) == 0 { panic("Key must be non-empty.") } // RFC 5297 defines S2V to handle an empty array, but never actually uses it // that way for encryption or decryption. Additionally, the s2v_* reference // functions don't handle that case. So don't handle it here. if len(strings) == 0 { panic("strings must be non-empty.") } // siv_init requires a full SIV key, i.e. twice the length of the key used by // S2V. It uses the first half for the S2V key. tmpKey := make([]byte, 2*len(key)) copy(tmpKey, key) key = tmpKey // Initialize the context struct. var ctx C.siv_ctx callResult := C.siv_init(&ctx, (*C.uchar)(&key[0]), C.int(8*len(key))) if callResult < 0 { panic("Error from siv_init.") } // Call s2v_update the requisite number of times. for i := 0; i < len(strings)-1; i++ { data := strings[i] dataLen := len(data) // Avoid indexing into an empty slice. if dataLen == 0 { data = make([]byte, 1) } C.s2v_update(&ctx, (*C.uchar)(&data[0]), C.int(dataLen)) } // Now finalize with the last string. Avoid indexing into an empty slice. lastString := strings[len(strings)-1] lastStringLen := len(lastString) if lastStringLen == 0 { lastString = make([]byte, 1) } cDigest := (*C.uchar)(C.malloc(16)) defer C.free(unsafe.Pointer(cDigest)) callResult = C.s2v_final( &ctx, (*C.uchar)(&lastString[0]), C.int(lastStringLen), cDigest) if callResult < 0 { panic("Error from s2v_final.") } return C.GoBytes(unsafe.Pointer(cDigest), 16) }
func encrypt(key, plaintext []byte, associated [][]byte) []byte { if len(key) == 0 { panic("Key must be non-empty.") } // Initialize the context struct. var ctx C.siv_ctx callResult := C.siv_init(&ctx, (*C.uchar)(&key[0]), C.int(8*len(key))) if callResult < 0 { panic("Error from siv_init.") } // Grab the right pointer for the plaintext, taking care not to index an // empty slice. var cPlaintext *C.uchar cPlaintextLen := C.int(len(plaintext)) if cPlaintextLen > 0 { cPlaintext = (*C.uchar)(&plaintext[0]) } // Create a buffer to store the SIV. cCounter := (*C.uchar)(C.malloc(16)) defer C.free(unsafe.Pointer(cCounter)) // Create associated data-related arguments. Take care not to index empty // slices. cNumAssociated := C.int(len(associated)) adLens := make([]C.int, cNumAssociated) ads := make([]*C.uchar, cNumAssociated) if cNumAssociated == 0 { adLens = make([]C.int, 1) ads = make([]*C.uchar, 1) } for i, _ := range associated { aLen := len(associated[i]) adLens[i] = C.int(aLen) if aLen > 0 { ads[i] = (*C.uchar)(&associated[i][0]) } } cAdLens := (*C.int)(&adLens[0]) cAds := (**C.uchar)(&ads[0]) // Call siv_encrypt. cCiphertext := (*C.uchar)(C.malloc(C.size_t(cPlaintextLen))) defer C.free(unsafe.Pointer(cCiphertext)) callResult = C.siv_encrypt( &ctx, cPlaintext, cCiphertext, cPlaintextLen, cCounter, cNumAssociated, cAdLens, cAds) if callResult < 0 { panic("Error from siv_encrypt.") } iv := C.GoBytes(unsafe.Pointer(cCounter), 16) ciphertext := C.GoBytes(unsafe.Pointer(cCiphertext), cPlaintextLen) return append(iv, ciphertext...) }