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:
parent
4b06c1b314
commit
4d0f3e5293
@ -2,4 +2,4 @@ sudo: required
|
||||
language: go
|
||||
|
||||
script:
|
||||
- make test
|
||||
- go test -v ./...
|
||||
|
@ -6,6 +6,8 @@ Crypto primitives implementation in Go.
|
||||
* hash/
|
||||
- cSHAKE (sha3 coppied from "golang.org/x/crypto")
|
||||
- SM3
|
||||
* rand/
|
||||
- CTR_DRBG with AES256 (NIST SP800-90A)
|
||||
|
||||
## Testing
|
||||
```
|
||||
|
153
drbg/ctr_drbg.go
Normal file
153
drbg/ctr_drbg.go
Normal 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
86
drbg/ctr_drbg_test.go
Normal 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()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user