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
|
language: go
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- make test
|
- go test -v ./...
|
||||||
|
@ -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
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