Merge pull request #24 from ccollins476ad/image-bin
image: Function to convert image to a byte slice
diff --git a/image/README.md b/image/README.md
new file mode 100644
index 0000000..b839e05
--- /dev/null
+++ b/image/README.md
@@ -0,0 +1,168 @@
+# Mynewt Images
+
+## Anatomy
+
+```
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Header |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ Padding (optional) ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ ~
+ ~ ~
+ ~ Body ~
+ ~ ~
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Protected Trailer (optional) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ ~
+ ~ Protected TLVs (optional) ~
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Trailer |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ ~
+ ~ TLVs ~
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+```
+
+All fields are in host-byte order (typically little endian).
+
+### Header
+
+```
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Magic (0x96f3b83d) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Reserved1 (0x00000000) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Header size | Protected size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Body size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Flags |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Major version | Minor version | Revision |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Build number |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Reserved2 (0x00000000) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+```
+
+| Field | Description | Notes |
+| ----- | ----------- | ----- |
+| Magic | Identifies the start of an image | |
+| Header size | 32 + the amount of padding that follows the header | |
+| Protected size | Size, in bytes, of the protected trailer PLUS the protected TLVs | 0 if no protected TLVs |
+| Body size | Size, in bytes, of the image body | |
+| Flags | One bit per flag | See below |
+| Major version | The first element of the version number | major.minor.revision.build |
+| Minor version | The second element of the version number | major.minor.revision.build |
+| Revision | The third element of the version number | major.minor.revision.build |
+| Build number | The fourth element of the version number | No meaning in semver |
+
+### Body
+
+The executable itself. In encrypted images, this is the encrypted portion.
+
+### Protected trailer
+
+Describes the set of protected TLVs that follow. This trailer is NOT present if there are no protected TLVs.
+
+```
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Magic (0x6908) | Protected size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+```
+
+| Field | Description | Notes |
+| ----- | ----------- | ----- |
+| Magic | Identifies the start of the protected trailer | |
+| Protected size | Size, in bytes, of the protected trailer PLUS the protected TLVs | Identical to "Protected size" in image header |
+
+### Protected TLVs
+
+A sequence of TLV structures (see "TLVs" section for specifics). The structure of these TLVs is identical to the non-protected TLVs. The difference is that these TLVs are included as input to the image hash.
+
+### Trailer
+
+Describes the set of TLVs that follow. This trailer is always present.
+
+```
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Magic (0x6907) | Size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+```
+
+| Field | Description | Notes |
+| ----- | ----------- | ----- |
+| Magic | Identifies the start of the trailer | |
+| Size | Size, in bytes, of the trailer PLUS the TLVs | |
+
+### TLVs
+
+The TLVs (type-length-value) are a sequence of variable length structures containing image metadata.
+
+```
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Type | Reserved (00) | Length |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ~ ~
+ ~ Body ~
+ ~ ~
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+```
+
+
+| Field | Description | Notes |
+| ----- | ----------- | ----- |
+| Type | Identifies the type of data in the TLV body | |
+| Length | The length, in bytes, of the TLV body | |
+| Body | Varies by type | |
+
+## Header flags
+
+Each header flag is represented by a single bit. As with the other numeric fields, the flags field is in host byte order.
+
+| Value | Description | Notes |
+| ----- | ----------- | ----- |
+| 0x00000004 | Encrypted by key in TLV | Implies the presence of an "enc" TLV |
+| 0x00000010 | Non-bootable | Second half of a split image |
+
+## TLV types
+
+| Value | Description | Notes |
+| ----- | ----------- | ----- |
+| 0x01 | Key hash | SHA256 of image verification key |
+| 0x10 | SHA256 | SHA256 of parts of the image (see below) |
+| 0x20 | Signature: RSA2048 | |
+| 0x21 | Signature: ECDSA224 | |
+| 0x22 | Signature: ECDSA256 | |
+| 0x23 | Signature: RSA3072 | |
+| 0x24 | Signature: ED25519 | |
+| 0x30 | Key-encrypting key: RSA | |
+| 0x31 | Key-encrypting key: KEK | |
+| 0x32 | Key-encrypting key: EC256 | |
+| 0x50 | Encryption nonce | |
+| 0x60 | Secret index | Indicates hardware-specific location of encryption key |
+
+### SHA256
+
+The sha256 is calculated using the following inputs:
+
+* Header
+* Post-header padding
+* Unencrypted image body
+* Protected trailer (if present)
+* Protected TLVs (if present)
diff --git a/image/create.go b/image/create.go
index 225a386..0217f2c 100644
--- a/image/create.go
+++ b/image/create.go
@@ -41,6 +41,7 @@
Body []byte
Version ImageVersion
SigKeys []sec.PrivSignKey
+ Sections []Section
HWKeyIndex int
Nonce []byte
PlainSecret []byte
@@ -48,6 +49,7 @@
HeaderSize int
InitialHash []byte
Bootable bool
+ UseLegacyTLV bool
}
type ImageCreateOpts struct {
@@ -56,9 +58,11 @@
SrcEncKeyIndex int
Version ImageVersion
SigKeys []sec.PrivSignKey
+ Sections []Section
LoaderHash []byte
HdrPad int
ImagePad int
+ UseLegacyTLV bool
}
type ECDSASig struct {
@@ -100,12 +104,21 @@
}
}
-func GenerateHWKeyIndexTLV(secretIndex uint32) (ImageTlv, error) {
+// GenerateHWKeyIndexTLV creates a hardware key index TLV.
+func GenerateHWKeyIndexTLV(secretIndex uint32, useLegacyTLV bool) (ImageTlv, error) {
+ var tlvType uint8
id := make([]byte, 4)
binary.LittleEndian.PutUint32(id, secretIndex)
+
+ if useLegacyTLV {
+ tlvType = IMAGE_TLV_SECRET_ID_LEGACY
+ } else {
+ tlvType = IMAGE_TLV_SECRET_ID
+ }
+
return ImageTlv{
Header: ImageTlvHdr{
- Type: IMAGE_TLV_SECRET_ID,
+ Type: tlvType,
Pad: 0,
Len: uint16(len(id)),
},
@@ -113,10 +126,19 @@
}, nil
}
-func GenerateNonceTLV(nonce []byte) (ImageTlv, error) {
+// GenerateNonceTLV creates a nonce TLV given a nonce.
+func GenerateNonceTLV(nonce []byte, useLegacyTLV bool) (ImageTlv, error) {
+ var tlvType uint8
+
+ if useLegacyTLV {
+ tlvType = IMAGE_TLV_AES_NONCE_LEGACY
+ } else {
+ tlvType = IMAGE_TLV_AES_NONCE
+ }
+
return ImageTlv{
Header: ImageTlvHdr{
- Type: IMAGE_TLV_AES_NONCE,
+ Type: tlvType,
Pad: 0,
Len: uint16(len(nonce)),
},
@@ -124,6 +146,7 @@
}, nil
}
+// GenerateEncTlv creates an encryption-secret TLV given a secret.
func GenerateEncTlv(cipherSecret []byte) (ImageTlv, error) {
var encType uint8
@@ -147,6 +170,25 @@
}, nil
}
+// GenerateEncTlv creates an encryption-secret TLV given a secret.
+func GenerateSectionTlv(section Section) (ImageTlv, error) {
+ data := make([]byte, 8+len(section.Name))
+
+ binary.LittleEndian.PutUint32(data[0:], uint32(section.Offset))
+ binary.LittleEndian.PutUint32(data[4:], uint32(section.Size))
+ copy(data[8:], section.Name)
+
+ return ImageTlv{
+ Header: ImageTlvHdr{
+ Type: IMAGE_TLV_SECTION,
+ Pad: 0,
+ Len: uint16(len(data)),
+ },
+ Data: data,
+ }, nil
+}
+
+// GenerateSig signs an image using an rsa key.
func GenerateSigRsa(key sec.PrivSignKey, hash []byte) ([]byte, error) {
opts := rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
@@ -160,6 +202,7 @@
return signature, nil
}
+// GenerateSig signs an image using an ec key.
func GenerateSigEc(key sec.PrivSignKey, hash []byte) ([]byte, error) {
r, s, err := ecdsa.Sign(rand.Reader, key.Ec, hash)
if err != nil {
@@ -187,6 +230,7 @@
return signature, nil
}
+// GenerateSig signs an image using an ed25519 key.
func GenerateSigEd25519(key sec.PrivSignKey, hash []byte) ([]byte, error) {
sig := ed25519.Sign(*key.Ed25519, hash)
@@ -199,6 +243,7 @@
return sig, nil
}
+// GenerateSig signs an image.
func GenerateSig(key sec.PrivSignKey, hash []byte) (sec.Sig, error) {
pub := key.PubKey()
typ, err := pub.SigType()
@@ -238,6 +283,8 @@
}, nil
}
+// BuildKeyHash produces a key-hash TLV given a public verification key. Users
+// do not normally need to call this. Call BuildSigTlvs instead.
func BuildKeyHashTlv(keyBytes []byte) ImageTlv {
data := sec.RawKeyHash(keyBytes)
return ImageTlv{
@@ -250,6 +297,8 @@
}
}
+// BuildSigTlvs signs an image and creates a pair of TLVs representing the
+// signature.
func BuildSigTlvs(keys []sec.PrivSignKey, hash []byte) ([]ImageTlv, error) {
var tlvs []ImageTlv
@@ -282,6 +331,7 @@
return tlvs, nil
}
+// GeneratePlainSecret randomly generates a 16-byte image-encrypting secret.
func GeneratePlainSecret() ([]byte, error) {
plainSecret := make([]byte, 16)
if _, err := rand.Read(plainSecret); err != nil {
@@ -291,6 +341,7 @@
return plainSecret, nil
}
+// GenerateImage produces an Image object from a set of image creation options.
func GenerateImage(opts ImageCreateOpts) (Image, error) {
ic := NewImageCreator()
@@ -303,6 +354,8 @@
ic.Version = opts.Version
ic.SigKeys = opts.SigKeys
ic.HWKeyIndex = opts.SrcEncKeyIndex
+ ic.Sections = opts.Sections
+ ic.UseLegacyTLV = opts.UseLegacyTLV
if opts.LoaderHash != nil {
ic.InitialHash = opts.LoaderHash
@@ -365,6 +418,7 @@
return ri, nil
}
+// calcHash calculates the sha256 for an image with the given components.
func calcHash(initialHash []byte, hdr ImageHdr, pad []byte,
plainBody []byte, protTlvs []ImageTlv) ([]byte, error) {
@@ -422,6 +476,7 @@
return hash.Sum(nil), nil
}
+// calcProtSize calculates the size, in bytes, of a set of protected TLVs.
func calcProtSize(protTlvs []ImageTlv) uint16 {
var size = uint16(0)
for _, tlv := range protTlvs {
@@ -434,6 +489,7 @@
return size
}
+// Create produces an Image object.
func (ic *ImageCreator) Create() (Image, error) {
img := Image{}
@@ -472,13 +528,22 @@
}
if ic.HWKeyIndex >= 0 {
- tlv, err := GenerateHWKeyIndexTLV(uint32(ic.HWKeyIndex))
+ tlv, err := GenerateHWKeyIndexTLV(uint32(ic.HWKeyIndex),
+ ic.UseLegacyTLV)
if err != nil {
return img, err
}
img.ProtTlvs = append(img.ProtTlvs, tlv)
- tlv, err = GenerateNonceTLV(ic.Nonce)
+ tlv, err = GenerateNonceTLV(ic.Nonce, ic.UseLegacyTLV)
+ if err != nil {
+ return img, err
+ }
+ img.ProtTlvs = append(img.ProtTlvs, tlv)
+ }
+
+ for s := range ic.Sections {
+ tlv, err := GenerateSectionTlv(ic.Sections[s])
if err != nil {
return img, err
}
@@ -488,19 +553,28 @@
img.Header.ProtSz = calcProtSize(img.ProtTlvs)
// Followed by data.
+ var hashBytes []byte
+ var err error
if ic.PlainSecret != nil {
+ // For encrypted images, must calculate the hash with the plain
+ // body and encrypt the payload afterwards
+ img.Body = append(img.Body, ic.Body...)
+ hashBytes, err = img.CalcHash(ic.InitialHash)
+ if err != nil {
+ return img, err
+ }
encBody, err := sec.EncryptAES(ic.Body, ic.PlainSecret, ic.Nonce)
if err != nil {
return img, err
}
+ img.Body = nil
img.Body = append(img.Body, encBody...)
} else {
img.Body = append(img.Body, ic.Body...)
- }
-
- hashBytes, err := img.CalcHash(ic.InitialHash)
- if err != nil {
- return img, err
+ hashBytes, err = img.CalcHash(ic.InitialHash)
+ if err != nil {
+ return img, err
+ }
}
// Hash TLV.
diff --git a/image/image.go b/image/image.go
index 90191fc..d54eb82 100644
--- a/image/image.go
+++ b/image/image.go
@@ -57,33 +57,39 @@
* Image trailer TLV types.
*/
const (
- IMAGE_TLV_KEYHASH = 0x01
- IMAGE_TLV_SHA256 = 0x10
- IMAGE_TLV_RSA2048 = 0x20
- IMAGE_TLV_ECDSA224 = 0x21
- IMAGE_TLV_ECDSA256 = 0x22
- IMAGE_TLV_RSA3072 = 0x23
- IMAGE_TLV_ED25519 = 0x24
- IMAGE_TLV_ENC_RSA = 0x30
- IMAGE_TLV_ENC_KEK = 0x31
- IMAGE_TLV_ENC_EC256 = 0x32
- IMAGE_TLV_AES_NONCE = 0x50
- IMAGE_TLV_SECRET_ID = 0x60
+ IMAGE_TLV_KEYHASH = 0x01
+ IMAGE_TLV_SHA256 = 0x10
+ IMAGE_TLV_RSA2048 = 0x20
+ IMAGE_TLV_ECDSA224 = 0x21
+ IMAGE_TLV_ECDSA256 = 0x22
+ IMAGE_TLV_RSA3072 = 0x23
+ IMAGE_TLV_ED25519 = 0x24
+ IMAGE_TLV_ENC_RSA = 0x30
+ IMAGE_TLV_ENC_KEK = 0x31
+ IMAGE_TLV_ENC_EC256 = 0x32
+ IMAGE_TLV_AES_NONCE_LEGACY = 0x50
+ IMAGE_TLV_SECRET_ID_LEGACY = 0x60
+ IMAGE_TLV_AES_NONCE = 0xa1
+ IMAGE_TLV_SECRET_ID = 0xa2
+ IMAGE_TLV_SECTION = 0xa3
)
var imageTlvTypeNameMap = map[uint8]string{
- IMAGE_TLV_KEYHASH: "KEYHASH",
- IMAGE_TLV_SHA256: "SHA256",
- IMAGE_TLV_RSA2048: "RSA2048",
- IMAGE_TLV_ECDSA224: "ECDSA224",
- IMAGE_TLV_ECDSA256: "ECDSA256",
- IMAGE_TLV_RSA3072: "RSA3072",
- IMAGE_TLV_ED25519: "ED25519",
- IMAGE_TLV_ENC_RSA: "ENC_RSA",
- IMAGE_TLV_ENC_KEK: "ENC_KEK",
- IMAGE_TLV_ENC_EC256: "ENC_EC256",
- IMAGE_TLV_AES_NONCE: "AES_NONCE",
- IMAGE_TLV_SECRET_ID: "SEC_KEY_ID",
+ IMAGE_TLV_KEYHASH: "KEYHASH",
+ IMAGE_TLV_SHA256: "SHA256",
+ IMAGE_TLV_RSA2048: "RSA2048",
+ IMAGE_TLV_ECDSA224: "ECDSA224",
+ IMAGE_TLV_ECDSA256: "ECDSA256",
+ IMAGE_TLV_RSA3072: "RSA3072",
+ IMAGE_TLV_ED25519: "ED25519",
+ IMAGE_TLV_ENC_RSA: "ENC_RSA",
+ IMAGE_TLV_ENC_KEK: "ENC_KEK",
+ IMAGE_TLV_ENC_EC256: "ENC_EC256",
+ IMAGE_TLV_AES_NONCE: "AES_NONCE",
+ IMAGE_TLV_SECRET_ID: "SEC_KEY_ID",
+ IMAGE_TLV_AES_NONCE_LEGACY: "AES_NONCE",
+ IMAGE_TLV_SECRET_ID_LEGACY: "SEC_KEY_ID",
+ IMAGE_TLV_SECTION: "SECTION",
}
var imageTlvTypeSigTypeMap = map[uint8]sec.SigType{
@@ -146,6 +152,12 @@
TotalSize int
}
+type Section struct {
+ Name string
+ Size int
+ Offset int
+}
+
func ImageTlvTypeIsValid(tlvType uint8) bool {
_, ok := imageTlvTypeNameMap[tlvType]
return ok
@@ -740,9 +752,15 @@
tlvs := dup.FindProtTlvs(IMAGE_TLV_AES_NONCE)
if len(tlvs) != 1 {
- return dup, errors.Errorf(
- "failed to decrypt hw-encrypted image: "+
- "wrong count of AES nonce TLVs; have=%d want=1", len(tlvs))
+ // try to find legacy TLV
+ tlvs := dup.FindProtTlvs(IMAGE_TLV_AES_NONCE_LEGACY)
+
+ if len(tlvs) != 1 {
+
+ return dup, errors.Errorf(
+ "failed to decrypt hw-encrypted image: "+
+ "wrong count of AES nonce TLVs; have=%d want=1", len(tlvs))
+ }
}
nonce := tlvs[0].Data
@@ -768,6 +786,8 @@
img.RemoveProtTlvsWithType(IMAGE_TLV_AES_NONCE)
img.RemoveProtTlvsWithType(IMAGE_TLV_SECRET_ID)
+ img.RemoveProtTlvsWithType(IMAGE_TLV_AES_NONCE_LEGACY)
+ img.RemoveProtTlvsWithType(IMAGE_TLV_SECRET_ID_LEGACY)
return img, nil
}
diff --git a/image/map.go b/image/map.go
index 947cb88..7e95aa0 100644
--- a/image/map.go
+++ b/image/map.go
@@ -31,6 +31,7 @@
"_offset": offset,
"flags": h.Flags,
"hdr_sz": h.HdrSz,
+ "prot_sz": h.ProtSz,
"img_sz": h.ImgSz,
"magic": h.Magic,
"vers": h.Vers.String(),
@@ -72,6 +73,12 @@
m := map[string]interface{}{}
m["header"] = img.Header.Map(offs.Header)
m["body"] = rawBodyMap(offs.Body)
+
+ if img.Header.ProtSz > 0 {
+ protTrailer := img.ProtTrailer()
+ m["prot_trailer"] = protTrailer.Map(offs.ProtTrailer)
+ }
+
trailer := img.Trailer()
m["trailer"] = trailer.Map(offs.Trailer)
diff --git a/sec/encrypt.go b/sec/encrypt.go
index 85d4571..97aaaec 100644
--- a/sec/encrypt.go
+++ b/sec/encrypt.go
@@ -31,9 +31,10 @@
"crypto/sha256"
"crypto/x509"
"encoding/base64"
- "golang.org/x/crypto/hkdf"
"io"
+ "golang.org/x/crypto/hkdf"
+
keywrap "github.com/NickBall/go-aes-key-wrap"
"github.com/apache/mynewt-artifact/errors"
)
@@ -251,17 +252,18 @@
}
func EncryptAES(plain []byte, secret []byte, nonce []byte) ([]byte, error) {
+ if len(nonce) > 16 {
+ return nil, errors.Errorf("AES nonce has invalid length: have=%d want<=16", len(nonce))
+ }
+
blk, err := aes.NewCipher(secret)
if err != nil {
return nil, errors.Errorf("Failed to create block cipher")
}
- var iv []byte
- if nonce == nil {
- iv = make([]byte, 16)
- } else {
- zeros := make([]byte, 8)
- iv = append(nonce, zeros...)
+ iv := nonce
+ for len(iv) < 16 {
+ iv = append(iv, 0)
}
stream := cipher.NewCTR(blk, iv)