gmsm/pkcs8/cipher.go
2022-06-17 10:59:23 +08:00

165 lines
3.8 KiB
Go

package pkcs8
import (
"crypto/cipher"
"crypto/rand"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"github.com/emmansun/gmsm/padding"
)
func genRandom(len int) ([]byte, error) {
value := make([]byte, len)
_, err := rand.Read(value)
return value, err
}
type cipherWithBlock struct {
oid asn1.ObjectIdentifier
ivSize int
keySize int
newBlock func(key []byte) (cipher.Block, error)
}
func (c cipherWithBlock) KeySize() int {
return c.keySize
}
func (c cipherWithBlock) OID() asn1.ObjectIdentifier {
return c.oid
}
func (c cipherWithBlock) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
block, err := c.newBlock(key)
if err != nil {
return nil, nil, err
}
iv, err := genRandom(c.ivSize)
if err != nil {
return nil, nil, err
}
ciphertext, err := cbcEncrypt(block, key, iv, plaintext)
if err != nil {
return nil, nil, err
}
marshalledIV, err := asn1.Marshal(iv)
if err != nil {
return nil, nil, err
}
encryptionScheme := pkix.AlgorithmIdentifier{
Algorithm: c.oid,
Parameters: asn1.RawValue{FullBytes: marshalledIV},
}
return &encryptionScheme, ciphertext, nil
}
func (c cipherWithBlock) Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error) {
block, err := c.newBlock(key)
if err != nil {
return nil, err
}
var iv []byte
if _, err := asn1.Unmarshal(parameters.FullBytes, &iv); err != nil {
return nil, errors.New("pkcs8: invalid cipher parameters")
}
return cbcDecrypt(block, key, iv, encryptedKey)
}
func cbcEncrypt(block cipher.Block, key, iv, plaintext []byte) ([]byte, error) {
mode := cipher.NewCBCEncrypter(block, iv)
pkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize()))
plainText := pkcs7.Pad(plaintext)
ciphertext := make([]byte, len(plainText))
mode.CryptBlocks(ciphertext, plainText)
return ciphertext, nil
}
func cbcDecrypt(block cipher.Block, key, iv, ciphertext []byte) ([]byte, error) {
mode := cipher.NewCBCDecrypter(block, iv)
pkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize()))
plaintext := make([]byte, len(ciphertext))
mode.CryptBlocks(plaintext, ciphertext)
return pkcs7.Unpad(plaintext)
}
type cipherWithGCM struct {
oid asn1.ObjectIdentifier
nonceSize int
keySize int
newBlock func(key []byte) (cipher.Block, error)
}
// http://javadoc.iaik.tugraz.at/iaik_jce/current/index.html?iaik/security/cipher/GCMParameters.html
type gcmParameters struct {
Nonce []byte `asn1:"tag:4"`
ICVLen int
}
func (c cipherWithGCM) KeySize() int {
return c.keySize
}
func (c cipherWithGCM) OID() asn1.ObjectIdentifier {
return c.oid
}
func (c cipherWithGCM) Encrypt(key, plaintext []byte) (*pkix.AlgorithmIdentifier, []byte, error) {
block, err := c.newBlock(key)
if err != nil {
return nil, nil, err
}
nonce, err := genRandom(c.nonceSize)
if err != nil {
return nil, nil, err
}
aead, err := cipher.NewGCMWithNonceSize(block, c.nonceSize)
if err != nil {
return nil, nil, err
}
ciphertext := aead.Seal(nil, nonce, plaintext, nil)
paramSeq := gcmParameters{
Nonce: nonce,
ICVLen: aead.Overhead(),
}
paramBytes, err := asn1.Marshal(paramSeq)
if err != nil {
return nil, nil, err
}
encryptionAlgorithm := pkix.AlgorithmIdentifier{
Algorithm: c.oid,
Parameters: asn1.RawValue{
Tag: asn1.TagSequence,
Bytes: paramBytes,
},
}
return &encryptionAlgorithm, ciphertext, nil
}
func (c cipherWithGCM) Decrypt(key []byte, parameters *asn1.RawValue, encryptedKey []byte) ([]byte, error) {
block, err := c.newBlock(key)
if err != nil {
return nil, err
}
params := gcmParameters{}
_, err = asn1.Unmarshal(parameters.Bytes, &params)
if err != nil {
return nil, err
}
aead, err := cipher.NewGCMWithNonceSize(block, len(params.Nonce))
if err != nil {
return nil, err
}
if params.ICVLen != aead.Overhead() {
return nil, errors.New("pkcs8: invalid tag size")
}
return aead.Open(nil, params.Nonce, encryptedKey, nil)
}