diff --git a/Cargo.toml b/Cargo.toml
index 97c2041e..76ba6960 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,5 +8,6 @@ members = [
     "kuznyechik",
     "magma",
     "rc2",
+    "serpent",
     "twofish",
 ]
diff --git a/serpent/Cargo.toml b/serpent/Cargo.toml
new file mode 100644
index 00000000..3c69bc96
--- /dev/null
+++ b/serpent/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "serpent"
+version = "0.6.0"
+authors = ["Blocs <jonathan@cryptoblocs.fr>"]
+license = "MIT/Apache-2.0"
+edition = "2018"
+description = "Serpent block cipher : A Candidate Block Cipher for the Advanced Encryption Standard (AES)"
+documentation = "https://docs.rs/serpent"
+repository = "https://github.com/RustCrypto/block-ciphers"
+keywords = ["crypto", "serpent", "block-cipher"]
+categories = ["cryptography", "no-std"]
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+byteorder = { version = "1", default-features = false }
+block-cipher-trait = "0.6"
+opaque-debug = "0.2"
+
+[dev-dependencies]
+block-cipher-trait = { version = "0.6", features = ["dev"] }
+
+[badges]
+travis-ci = { repository = "RustCrypto/block-ciphers" }
diff --git a/serpent/LICENSE-APACHE b/serpent/LICENSE-APACHE
new file mode 100644
index 00000000..338c89c7
--- /dev/null
+++ b/serpent/LICENSE-APACHE
@@ -0,0 +1,13 @@
+Copyright 2019 Jonathan Serra
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/serpent/LICENSE-MIT b/serpent/LICENSE-MIT
new file mode 100644
index 00000000..6e0aa2fe
--- /dev/null
+++ b/serpent/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2019 Jonathan Serra
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/serpent/src/consts.rs b/serpent/src/consts.rs
new file mode 100644
index 00000000..60537030
--- /dev/null
+++ b/serpent/src/consts.rs
@@ -0,0 +1,25 @@
+pub const PHI: u32 = 0x9e37_79b9;
+pub const ROUNDS: usize = 32;
+
+// Serpent-1 SBoxes
+pub const S: [[u8; 16]; 8] = [
+    [3, 8, 15, 1, 10, 6, 5, 11, 14, 13, 4, 2, 7, 0, 9, 12], // S0
+    [15, 12, 2, 7, 9, 0, 5, 10, 1, 11, 14, 8, 6, 13, 3, 4], // S1
+    [8, 6, 7, 9, 3, 12, 10, 15, 13, 1, 14, 4, 0, 11, 5, 2], // S2
+    [0, 15, 11, 8, 12, 9, 6, 3, 13, 1, 2, 4, 10, 7, 5, 14], // S3
+    [1, 15, 8, 3, 12, 0, 11, 6, 2, 5, 4, 10, 9, 14, 7, 13], // S4
+    [15, 5, 2, 11, 4, 10, 9, 12, 0, 3, 14, 8, 13, 6, 7, 1], // S5
+    [7, 2, 12, 5, 8, 4, 6, 11, 14, 9, 1, 15, 13, 3, 10, 0], // S6
+    [1, 13, 15, 0, 14, 8, 2, 11, 7, 4, 12, 10, 9, 3, 5, 6], // S7
+];
+
+pub const S_INVERSE: [[u8; 16]; 8] = [
+    [13, 3, 11, 0, 10, 6, 5, 12, 1, 14, 4, 7, 15, 9, 8, 2], // InvS0
+    [5, 8, 2, 14, 15, 6, 12, 3, 11, 4, 7, 9, 1, 13, 10, 0], // InvS1
+    [12, 9, 15, 4, 11, 14, 1, 2, 0, 3, 6, 13, 5, 8, 10, 7], // InvS2
+    [0, 9, 10, 7, 11, 14, 6, 13, 3, 5, 12, 2, 4, 8, 15, 1], // InvS3
+    [5, 0, 8, 3, 10, 9, 7, 14, 2, 12, 11, 6, 4, 15, 13, 1], // InvS4
+    [8, 15, 2, 9, 4, 1, 13, 14, 11, 6, 5, 3, 7, 12, 10, 0], // InvS5
+    [15, 10, 1, 13, 5, 3, 6, 0, 4, 9, 14, 7, 2, 12, 8, 11], // InvS6
+    [3, 0, 6, 13, 9, 14, 15, 8, 5, 12, 11, 7, 10, 1, 4, 2], // InvS7
+];
diff --git a/serpent/src/lib.rs b/serpent/src/lib.rs
new file mode 100644
index 00000000..5a2071c2
--- /dev/null
+++ b/serpent/src/lib.rs
@@ -0,0 +1,271 @@
+//! An implementation of the [Serpent1][1] block cipher.
+//! Inspired by [Serpent reference implementation][2] and [Lars Viklund Rust implementation][3].
+//! [1]: https://www.cl.cam.ac.uk/~rja14/Papers/serpent.pdf
+//! [2]: https://www.cl.cam.ac.uk/~fms27/serpent/
+//! [3]: https://github.com/efb9-860a-e752-0dac/serpent
+// #![no_std]
+pub extern crate block_cipher_trait;
+extern crate byteorder;
+#[macro_use]
+extern crate opaque_debug;
+
+use block_cipher_trait::generic_array::typenum::{U1, U16};
+use block_cipher_trait::generic_array::GenericArray;
+use block_cipher_trait::BlockCipher;
+use block_cipher_trait::InvalidKeyLength;
+use byteorder::{ByteOrder, LE};
+
+mod consts;
+use consts::{PHI, ROUNDS, S, S_INVERSE};
+
+type Key = [u8; 16];
+type Subkeys = [Key; ROUNDS + 1];
+type Block128 = [u8; 16];
+type Word = [u8; 16];
+
+#[derive(Clone)]
+pub struct Serpent {
+    k: Subkeys,
+}
+
+fn get_bit(x: usize, i: usize) -> u8 {
+    (x >> i) as u8 & 0x01
+}
+
+fn linear_transform_bitslice(input: Block128, output: &mut Block128) {
+    let mut words = [0u32; 4];
+    LE::read_u32_into(&input, &mut words);
+
+    words[0] = words[0].rotate_left(13);
+    words[2] = words[2].rotate_left(3);
+    words[1] ^= words[0] ^ words[2];
+    words[3] = words[3] ^ words[2] ^ (words[0] << 3);
+    words[1] = words[1].rotate_left(1);
+    words[3] = words[3].rotate_left(7);
+    words[0] ^= words[1] ^ words[3];
+    words[2] = words[2] ^ words[3] ^ (words[1] << 7);
+    words[0] = words[0].rotate_left(5);
+    words[2] = words[2].rotate_left(22);
+
+    LE::write_u32_into(&words, output);
+}
+fn linear_transform_inverse_bitslice(input: Block128, output: &mut Block128) {
+    let mut words = [0u32; 4];
+    LE::read_u32_into(&input, &mut words);
+    words[2] = words[2].rotate_right(22);
+    words[0] = words[0].rotate_right(5);
+    words[2] = words[2] ^ words[3] ^ (words[1] << 7);
+    words[0] ^= words[1] ^ words[3];
+    words[3] = words[3].rotate_right(7);
+    words[1] = words[1].rotate_right(1);
+    words[3] = words[3] ^ words[2] ^ (words[0] << 3);
+    words[1] ^= words[0] ^ words[2];
+    words[2] = words[2].rotate_right(3);
+    words[0] = words[0].rotate_right(13);
+
+    LE::write_u32_into(&words, output);
+}
+
+fn round_bitslice(
+    i: usize,
+    b_i: Block128,
+    k: Subkeys,
+    b_output: &mut Block128,
+) {
+    let xored_block = xor_block(b_i, k[i]);
+
+    let s_i = apply_s_bitslice(i, xored_block);
+
+    if i == ROUNDS - 1 {
+        *b_output = xor_block(s_i, k[ROUNDS]);
+    } else {
+        linear_transform_bitslice(s_i, b_output);
+    }
+}
+fn round_inverse_bitslice(
+    i: usize,
+    b_i_next: Block128,
+    k: Subkeys,
+    b_output: &mut Block128,
+) {
+    let mut s_i = [0u8; 16];
+    if i == ROUNDS - 1 {
+        s_i = xor_block(b_i_next, k[ROUNDS]);
+    } else {
+        linear_transform_inverse_bitslice(b_i_next, &mut s_i);
+    }
+
+    let xored = apply_s_inverse_bitslice(i, s_i);
+
+    *b_output = xor_block(xored, k[i]);
+}
+
+fn apply_s(index: usize, nibble: u8) -> u8 {
+    S[index % 8][nibble as usize]
+}
+fn apply_s_inverse(index: usize, nibble: u8) -> u8 {
+    S_INVERSE[index % 8][nibble as usize]
+}
+
+fn apply_s_bitslice(index: usize, word: Word) -> Word {
+    let mut output = [0u8; 16];
+    let w1 = LE::read_u32(&word[0..4]);
+    let w2 = LE::read_u32(&word[4..8]);
+    let w3 = LE::read_u32(&word[8..12]);
+    let w4 = LE::read_u32(&word[12..16]);
+    let mut words = [0u32; 4];
+    for i in 0..32 {
+        let quad = apply_s(
+            index,
+            get_bit(w1 as usize, i)
+                | get_bit(w2 as usize, i) << 1
+                | get_bit(w3 as usize, i) << 2
+                | get_bit(w4 as usize, i) << 3,
+        );
+        for l in 0..4 {
+            words[l] |= u32::from(get_bit(quad as usize, l)) << i;
+        }
+    }
+    LE::write_u32_into(&words, &mut output);
+    output
+}
+fn apply_s_inverse_bitslice(index: usize, word: Word) -> Word {
+    let mut output = [0u8; 16];
+    let w1 = LE::read_u32(&word[0..4]);
+    let w2 = LE::read_u32(&word[4..8]);
+    let w3 = LE::read_u32(&word[8..12]);
+    let w4 = LE::read_u32(&word[12..16]);
+    let mut words = [0u32; 4];
+    for i in 0..32 {
+        let quad = apply_s_inverse(
+            index,
+            get_bit(w1 as usize, i)
+                | get_bit(w2 as usize, i) << 1
+                | get_bit(w3 as usize, i) << 2
+                | get_bit(w4 as usize, i) << 3,
+        );
+        for l in 0..4 {
+            words[l] |= u32::from(get_bit(quad as usize, l)) << i;
+        }
+    }
+    LE::write_u32_into(&words, &mut output);
+    output
+}
+
+fn xor_block(b1: Block128, k: Key) -> Block128 {
+    let mut xored: Block128 = [0u8; 16];
+    for (i, _) in b1.iter().enumerate() {
+        xored[i] = b1[i] ^ k[i];
+    }
+    xored
+}
+
+fn expand_key(source: &[u8], len_bits: usize, key: &mut [u8; 32]) {
+    key[..source.len()].copy_from_slice(&source);
+    if len_bits < 256 {
+        let byte_i = len_bits / 8;
+        let bit_i = len_bits % 8;
+        key[byte_i] |= 1 << bit_i;
+    }
+}
+
+impl Serpent {
+    fn key_schedule(key: [u8; 32]) -> Subkeys {
+        let mut words = [0u32; 140];
+
+        LE::read_u32_into(&key, &mut words[..8]);
+
+        for i in 0..132 {
+            let slot = i + 8;
+            words[slot] = (words[slot - 8]
+                ^ words[slot - 5]
+                ^ words[slot - 3]
+                ^ words[slot - 1]
+                ^ PHI
+                ^ i as u32)
+                .rotate_left(11);
+        }
+
+        let r = ROUNDS + 1;
+        let words = &words[8..];
+        let mut k = [0u32; 132];
+        for i in 0..r {
+            let sbox_index = (ROUNDS + 3 - i) % ROUNDS;
+            let a = words[(4 * i + 0) as usize];
+            let b = words[(4 * i + 1) as usize];
+            let c = words[(4 * i + 2) as usize];
+            let d = words[(4 * i + 3) as usize];
+            // calculate keys in bitslicing mode
+            for j in 0..32 {
+                let input = get_bit(a as usize, j)
+                    | get_bit(b as usize, j) << 1
+                    | get_bit(c as usize, j) << 2
+                    | get_bit(d as usize, j) << 3;
+                let output = apply_s(sbox_index, input as u8);
+                for l in 0..4 {
+                    k[(4 * i + l) as usize] |=
+                        u32::from(get_bit(output as usize, l)) << j;
+                }
+            }
+        }
+
+        let mut sub_keys: Subkeys = [[0u8; 16]; ROUNDS + 1];
+        for i in 0..r {
+            LE::write_u32(&mut sub_keys[i][..4], k[4 * i]);
+            LE::write_u32(&mut sub_keys[i][4..8], k[4 * i + 1]);
+            LE::write_u32(&mut sub_keys[i][8..12], k[4 * i + 2]);
+            LE::write_u32(&mut sub_keys[i][12..], k[4 * i + 3]);
+        }
+
+        sub_keys
+    }
+}
+
+impl BlockCipher for Serpent {
+    type KeySize = U16;
+    type BlockSize = U16;
+    type ParBlocks = U1;
+    fn new(key: &GenericArray<u8, U16>) -> Self {
+        Self::new_varkey(key).unwrap()
+    }
+
+    fn new_varkey(key: &[u8]) -> Result<Self, InvalidKeyLength> {
+        if key.len() < 16 || key.len() > 32 {
+            return Err(InvalidKeyLength);
+        }
+        let mut k = [0u8; 32];
+        expand_key(key, key.len() * 8, &mut k);
+        Ok(Serpent {
+            k: Serpent::key_schedule(k),
+        })
+    }
+
+    fn encrypt_block(&self, block: &mut GenericArray<u8, Self::BlockSize>) {
+        let mut b = [0u8; 16];
+
+        for (i, v) in block.iter().enumerate() {
+            b[i] = *v;
+        }
+
+        for i in 0..ROUNDS {
+            round_bitslice(i, b, self.k, &mut b);
+        }
+        *block = *GenericArray::from_slice(&b);
+    }
+
+    fn decrypt_block(&self, block: &mut GenericArray<u8, Self::BlockSize>) {
+        let mut b = [0u8; 16];
+
+        for (i, v) in block.iter().enumerate() {
+            b[i] = *v;
+        }
+
+        for i in (0..ROUNDS).rev() {
+            round_inverse_bitslice(i, b, self.k, &mut b);
+        }
+
+        *block = *GenericArray::from_slice(&b);
+    }
+}
+
+impl_opaque_debug!(Serpent);
diff --git a/serpent/tests/data/serpent_key_128bits.blb b/serpent/tests/data/serpent_key_128bits.blb
new file mode 100644
index 00000000..c61dafb3
Binary files /dev/null and b/serpent/tests/data/serpent_key_128bits.blb differ
diff --git a/serpent/tests/data/serpent_key_192bits.blb b/serpent/tests/data/serpent_key_192bits.blb
new file mode 100644
index 00000000..b46c2b85
Binary files /dev/null and b/serpent/tests/data/serpent_key_192bits.blb differ
diff --git a/serpent/tests/data/serpent_key_256bits.blb b/serpent/tests/data/serpent_key_256bits.blb
new file mode 100644
index 00000000..59b55919
Binary files /dev/null and b/serpent/tests/data/serpent_key_256bits.blb differ
diff --git a/serpent/tests/lib.rs b/serpent/tests/lib.rs
new file mode 100644
index 00000000..bfac1f8a
--- /dev/null
+++ b/serpent/tests/lib.rs
@@ -0,0 +1,10 @@
+//! Test vectors are from Nessie
+//! http://www.cs.technion.ac.il/~biham/Reports/Serpent/Serpent-128-128.verified.test-vectors
+#![no_std]
+extern crate serpent;
+#[macro_use]
+extern crate block_cipher_trait;
+
+new_test!(serpent_test_128, "serpent_key_128bits", serpent::Serpent);
+new_test!(serpent_test_192, "serpent_key_192bits", serpent::Serpent);
+new_test!(serpent_test_256, "serpent_key_256bits", serpent::Serpent);
