1
0
mirror of https://github.com/henrydcase/nobs.git synced 2024-11-26 00:51:22 +00:00

AES-256 CTR_DRBG

This commit is contained in:
Henry Case 2018-06-24 00:08:53 +01:00
parent 4b06c1b314
commit 4d0f3e5293
6 changed files with 242 additions and 75 deletions

View File

@ -2,4 +2,4 @@ sudo: required
language: go language: go
script: script:
- make test - go test -v ./...

View File

@ -6,6 +6,8 @@ Crypto primitives implementation in Go.
* hash/ * hash/
- cSHAKE (sha3 coppied from "golang.org/x/crypto") - cSHAKE (sha3 coppied from "golang.org/x/crypto")
- SM3 - SM3
* rand/
- CTR_DRBG with AES256 (NIST SP800-90A)
## Testing ## Testing
``` ```

153
drbg/ctr_drbg.go Normal file
View File

@ -0,0 +1,153 @@
// This is initial implementation of CTR_DRBG with AES-256. Code is tested
// and functionaly correct. Nevertheless it will be changed
//
// TODO: Following things still need to be done
// * Add other AES key lengts
// * Validate sizes from table 3 of SP800-90A
// * Improve reseeding so that code returns an error when reseed is needed
// * Add case with derivation function (maybe)
// * Code cleanup
// * Implement benchmark
// * Add rest of the test vectors from CAVP
package drbg
import (
"crypto/aes"
)
// Constants below correspond to AES-256, which is currently
// the only block cipher supported.
const (
BlockLen = 16
KeyLen = 32
SeedLen = BlockLen + KeyLen
)
type CtrDrbg struct {
v []byte
key []byte
counter uint
strength uint
resistance bool
}
func NewCtrDrbg() *CtrDrbg {
var c = new(CtrDrbg)
// Security strength for AES-256 as per SP800-57, 5.6.1
c.strength = 256
return c
}
func (c *CtrDrbg) inc() {
for i := BlockLen - 1; i >= 0; i-- {
if c.v[i] == 0xff {
c.v[i] = 0x00
} else {
c.v[i]++
break
}
}
}
func (c *CtrDrbg) Init(entropy, personalization []byte) bool {
var lsz int
var seedBuf [SeedLen]byte
// Minimum entropy input (SP800-90A, 10.2.1)
if len(entropy) < int(c.strength/8) {
return false
}
lsz = len(entropy)
if lsz > SeedLen {
lsz = SeedLen
}
copy(seedBuf[:], entropy[:lsz])
lsz = len(personalization)
if lsz > SeedLen {
lsz = SeedLen
}
for i := 0; i < lsz; i++ {
seedBuf[i] ^= personalization[i]
}
c.key = make([]byte, KeyLen)
c.v = make([]byte, BlockLen)
c.update(seedBuf[:])
c.counter = 1
return true
}
func (c *CtrDrbg) update(data []byte) {
var buf [3 * BlockLen]byte
if len(data) != SeedLen {
// OZAPTF: panic?
panic("Provided data is not equal to strength/8")
}
for i := 0; i < 3*BlockLen; i += BlockLen {
c.inc()
// Ignore error => NewCipher returns error when c.key has unexpected size
encBlock, _ := aes.NewCipher(c.key)
encBlock.Encrypt(buf[i:], c.v)
}
for i := 0; i < len(buf); i++ {
buf[i] ^= data[i]
}
copy(c.key, buf[:KeyLen])
copy(c.v, buf[KeyLen:])
}
func (c *CtrDrbg) Reseed(entropy, data []byte) {
var seedBuf [SeedLen]byte
var lsz int
lsz = len(entropy)
if lsz > SeedLen {
lsz = SeedLen
}
copy(seedBuf[:], entropy[:lsz])
lsz = len(data)
if lsz > SeedLen {
lsz = SeedLen
}
for i := 0; i < lsz; i++ {
seedBuf[i] ^= data[i]
}
c.update(seedBuf[:])
c.counter = 1
}
func (c *CtrDrbg) Read(b, ad []byte) (n int, err error) {
var seedBuf [SeedLen]byte
// TODO: check reseed_counter > reseed_interval
if len(ad) > 0 {
// pad additional data with zeros if needed
copy(seedBuf[:], ad)
c.update(seedBuf[:])
}
// OZAPTF: would be better not need to allocate that
buf := make([]byte, ((len(b)+BlockLen)/BlockLen)*BlockLen)
for i := 0; i < len(b); i += BlockLen {
c.inc()
// Ignore error => NewCipher returns error when c.key has unexpected size
encBlock, _ := aes.NewCipher(c.key)
encBlock.Encrypt(buf[i:], c.v)
}
copy(b, buf[:len(b)])
c.update(seedBuf[:])
c.counter += 1
return len(b), nil
}

86
drbg/ctr_drbg_test.go Normal file
View File

@ -0,0 +1,86 @@
package drbg
import (
"bytes"
"encoding/hex"
"testing"
)
func S2H(s string) []byte {
hex, e := hex.DecodeString(s)
if e != nil {
panic("Can't import private key")
}
return hex
}
func TestNominal(t *testing.T) {
var entropy [16]byte
var data [48]byte
c := NewCtrDrbg()
if !c.Init(entropy[:], nil) {
t.FailNow()
}
c.Read(entropy[0:16], data[:])
exp := S2H("16BA361FA14563FB1E8BCF88932F9FA7")
if !bytes.Equal(exp, entropy[:]) {
t.FailNow()
}
}
// TODO: should parse *.req file from here: https://raw.githubusercontent.com/coruus/nist-testvectors/master/csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors/drbgvectors_pr_false/CTR_DRBG.rsp
var vectors = []struct {
EntropyInput []byte
PersonalizationString []byte
EntropyInputReseed []byte
AdditionalInputReseed []byte
AdditionalInput1 []byte
AdditionalInput2 []byte
ReturnedBits []byte
}{
// With Reseeding
{
S2H("99903165903fea49c2db26ed675e44cc14cb2c1f28b836b203240b02771e831146ffc4335373bb344688c5c950670291"),
[]byte{},
S2H("b4ee99fa9e0eddaf4a3612013cd636c4af69177b43eebb3c58a305b9979b68b5cc820504f6c029aad78a5d29c66e84a0"),
S2H("2d8c5c28b05696e74774eb69a10f01c5fabc62691ddf7848a8004bb5eeb4d2c5febe1aa01f4d557b23d7e9a0e4e90655"),
S2H("0dc9cde42ac6e856f01a55f219c614de90c659260948db5053d414bab0ec2e13e995120c3eb5aafc25dc4bdcef8ace24"),
S2H("711be6c035013189f362211889248ca8a3268e63a7eb26836d915810a680ac4a33cd1180811a31a0f44f08db3dd64f91"),
S2H("11c7a0326ea737baa7a993d510fafee5374e7bbe17ef0e3e29f50fa68aac2124b017d449768491cac06d136d691a4e80785739f9aaedf311bba752a3268cc531"),
},
{
S2H("ffad10100025a879672ff50374b286712f457dd01441d76ac1a1cd15c7390dd93179a2f5920d198bf34a1b76fbc21289"),
S2H("1d2be6f25e88fa30c4ef42e4d54efd957dec231fa00143ca47580be666a8c143a916c90b3819a0a7ea914e3c9a2e7a3f"),
S2H("6c1a089cae313363bc76a780139eb4f2f2048b1f6b07896c5c412bff0385440fc43b73facbb79e3a252fa01fe17ab391"),
[]byte{},
[]byte{},
[]byte{},
S2H("e053c7d4bd9099ef6a99f190a5fd80219437d642006672338da6e0fe73ca4d24ffa51151bfbdac78d8a2f6255046edf57a04626e9977139c6933274299f3bdff"),
},
}
func TestVector(t *testing.T) {
for i := range vectors {
result := make([]byte, len(vectors[i].ReturnedBits))
c := NewCtrDrbg()
if !c.Init(vectors[i].EntropyInput[:], vectors[i].PersonalizationString) {
t.FailNow()
}
if len(vectors[i].EntropyInputReseed) > 0 {
c.Reseed(vectors[i].EntropyInputReseed[:], vectors[i].AdditionalInputReseed[:])
}
c.Read(result[:], vectors[i].AdditionalInput1)
c.Read(result[:], vectors[i].AdditionalInput2)
if !bytes.Equal(vectors[i].ReturnedBits[:], result[:]) {
t.FailNow()
}
}
}

View File

@ -1,53 +0,0 @@
import rand
import (
"crypto/aes"
"crypto/cipher"
)
// Constants below correspond to AES-256, which is currently
// the only block cipher supported.
const {
Blocklen = 16
Keylen = 32
}
type CtrDrbg struct {
v uint
keylen uint // OZAPTF: is it needed?
counter uint
strength uint
resistance bool
}
func (c *CtrDrbg) update(data []byte) {
}
func New() *CtrDrbg {
c = new(CtrDrbg)
c.key = make([]byte, 0, Keylen)
c.v = make([]byte, 0, Blocklen)
// Security strength for AES-256 as per SP800-57, 5.6.1
c.strength = 256
return c
}
func (c *CtrDrbg) Init(entropy []byte, personalization []byte, strength uint) bool {
if len(entropy) < (c.strength/8) {
return nil
}
// does enropyt needs to have some minimal length?
seed := make([]byte, 0, c.strength / 8)
c.update(seed)
c.counter = 1
return c
}
func (c *CtrDrbg) Update() {}
func (c *CtrDrbg) Read(b []byte) (n int, err error) {
}

View File

@ -1,21 +0,0 @@
import rand
import (
"testing"
"fmt"
"io"
"os"
"crypto/aes"
"crypto/cipher"
)
func TestNominal(t* testing.T) {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(pt, ct)
}