package symm import ( "crypto/cipher" "crypto/rand" "errors" "io" "b612.me/starcrypto/ccm" "github.com/emmansun/gmsm/sm4" ) func EncryptSM4(data, key, iv []byte, mode, paddingType string) ([]byte, error) { normalizedMode := normalizeCipherMode(mode) if normalizedMode == "" { normalizedMode = MODEGCM } if normalizedMode == MODEGCM { return EncryptSM4GCM(data, key, iv, nil) } if normalizedMode == MODECCM { return EncryptSM4CCM(data, key, iv, nil) } block, err := sm4.NewCipher(key) if err != nil { return nil, err } return encryptWithBlockMode(block, data, iv, normalizedMode, paddingType, PKCS7PADDING) } func DecryptSM4(src, key, iv []byte, mode, paddingType string) ([]byte, error) { normalizedMode := normalizeCipherMode(mode) if normalizedMode == "" { normalizedMode = MODEGCM } if normalizedMode == MODEGCM { return DecryptSM4GCM(src, key, iv, nil) } if normalizedMode == MODECCM { return DecryptSM4CCM(src, key, iv, nil) } block, err := sm4.NewCipher(key) if err != nil { return nil, err } return decryptWithBlockMode(block, src, iv, normalizedMode, paddingType, PKCS7PADDING) } func EncryptSM4Stream(dst io.Writer, src io.Reader, key, iv []byte, mode, paddingType string) error { normalizedMode := normalizeCipherMode(mode) if normalizedMode == "" { normalizedMode = MODEGCM } if normalizedMode == MODEGCM { return EncryptSM4GCMStream(dst, src, key, iv, nil) } if normalizedMode == MODECCM { return EncryptSM4CCMStream(dst, src, key, iv, nil) } block, err := sm4.NewCipher(key) if err != nil { return err } return encryptWithBlockModeStream(block, dst, src, iv, normalizedMode, paddingType, PKCS7PADDING) } func DecryptSM4Stream(dst io.Writer, src io.Reader, key, iv []byte, mode, paddingType string) error { normalizedMode := normalizeCipherMode(mode) if normalizedMode == "" { normalizedMode = MODEGCM } if normalizedMode == MODEGCM { return DecryptSM4GCMStream(dst, src, key, iv, nil) } if normalizedMode == MODECCM { return DecryptSM4CCMStream(dst, src, key, iv, nil) } block, err := sm4.NewCipher(key) if err != nil { return err } return decryptWithBlockModeStream(block, dst, src, iv, normalizedMode, paddingType, PKCS7PADDING) } func EncryptSM4WithOptions(data, key []byte, opts *CipherOptions) ([]byte, error) { cfg := normalizeCipherOptions(opts) mode := normalizeCipherMode(cfg.Mode) if mode == "" { mode = MODEGCM } if mode == MODEGCM { return EncryptSM4GCM(data, key, nonceFromOptions(cfg), cfg.AAD) } if mode == MODECCM { return EncryptSM4CCM(data, key, nonceFromOptions(cfg), cfg.AAD) } return EncryptSM4(data, key, cfg.IV, mode, cfg.Padding) } func DecryptSM4WithOptions(src, key []byte, opts *CipherOptions) ([]byte, error) { cfg := normalizeCipherOptions(opts) mode := normalizeCipherMode(cfg.Mode) if mode == "" { mode = MODEGCM } if mode == MODEGCM { return DecryptSM4GCM(src, key, nonceFromOptions(cfg), cfg.AAD) } if mode == MODECCM { return DecryptSM4CCM(src, key, nonceFromOptions(cfg), cfg.AAD) } return DecryptSM4(src, key, cfg.IV, mode, cfg.Padding) } func EncryptSM4StreamWithOptions(dst io.Writer, src io.Reader, key []byte, opts *CipherOptions) error { cfg := normalizeCipherOptions(opts) mode := normalizeCipherMode(cfg.Mode) if mode == "" { mode = MODEGCM } if mode == MODEGCM { return EncryptSM4GCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD) } if mode == MODECCM { return EncryptSM4CCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD) } return EncryptSM4Stream(dst, src, key, cfg.IV, mode, cfg.Padding) } func DecryptSM4StreamWithOptions(dst io.Writer, src io.Reader, key []byte, opts *CipherOptions) error { cfg := normalizeCipherOptions(opts) mode := normalizeCipherMode(cfg.Mode) if mode == "" { mode = MODEGCM } if mode == MODEGCM { return DecryptSM4GCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD) } if mode == MODECCM { return DecryptSM4CCMStream(dst, src, key, nonceFromOptions(cfg), cfg.AAD) } return DecryptSM4Stream(dst, src, key, cfg.IV, mode, cfg.Padding) } func EncryptSM4GCM(plain, key, nonce, aad []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } if len(nonce) != gcm.NonceSize() { return nil, ErrInvalidGCMNonceLength } return gcm.Seal(nil, nonce, plain, aad), nil } func DecryptSM4GCM(ciphertext, key, nonce, aad []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } if len(nonce) != gcm.NonceSize() { return nil, ErrInvalidGCMNonceLength } return gcm.Open(nil, nonce, ciphertext, aad) } func EncryptSM4GCMChunk(plain, key, nonce, aad []byte, chunkIndex uint64) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } if len(nonce) != gcm.NonceSize() { return nil, ErrInvalidGCMNonceLength } return encryptGCMChunk(gcm, plain, nonce, aad, chunkIndex), nil } func DecryptSM4GCMChunk(ciphertext, key, nonce, aad []byte, chunkIndex uint64) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } if len(nonce) != gcm.NonceSize() { return nil, ErrInvalidGCMNonceLength } return decryptGCMChunk(gcm, ciphertext, nonce, aad, chunkIndex) } func EncryptSM4GCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error { block, err := sm4.NewCipher(key) if err != nil { return err } gcm, err := cipher.NewGCM(block) if err != nil { return err } if len(nonce) != gcm.NonceSize() { return ErrInvalidGCMNonceLength } return encryptGCMChunkedStream(dst, src, gcm, nonce, aad) } func DecryptSM4GCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error { block, err := sm4.NewCipher(key) if err != nil { return err } gcm, err := cipher.NewGCM(block) if err != nil { return err } if len(nonce) != gcm.NonceSize() { return ErrInvalidGCMNonceLength } return decryptGCMChunkedOrLegacyStream(dst, src, gcm, nonce, aad) } func newSM4CCM(key []byte) (cipher.AEAD, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } return ccm.NewCCM(block, aeadCCMTagSize, aeadCCMNonceSize) } func EncryptSM4CCM(plain, key, nonce, aad []byte) ([]byte, error) { aead, err := newSM4CCM(key) if err != nil { return nil, err } if len(nonce) != aead.NonceSize() { return nil, ErrInvalidCCMNonceLength } return aead.Seal(nil, nonce, plain, aad), nil } func DecryptSM4CCM(ciphertext, key, nonce, aad []byte) ([]byte, error) { aead, err := newSM4CCM(key) if err != nil { return nil, err } if len(nonce) != aead.NonceSize() { return nil, ErrInvalidCCMNonceLength } return aead.Open(nil, nonce, ciphertext, aad) } func EncryptSM4CCMChunk(plain, key, nonce, aad []byte, chunkIndex uint64) ([]byte, error) { aead, err := newSM4CCM(key) if err != nil { return nil, err } if len(nonce) != aead.NonceSize() { return nil, ErrInvalidCCMNonceLength } return encryptCCMChunk(aead, plain, nonce, aad, chunkIndex), nil } func DecryptSM4CCMChunk(ciphertext, key, nonce, aad []byte, chunkIndex uint64) ([]byte, error) { aead, err := newSM4CCM(key) if err != nil { return nil, err } if len(nonce) != aead.NonceSize() { return nil, ErrInvalidCCMNonceLength } return decryptCCMChunk(aead, ciphertext, nonce, aad, chunkIndex) } func EncryptSM4CCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error { aead, err := newSM4CCM(key) if err != nil { return err } if len(nonce) != aead.NonceSize() { return ErrInvalidCCMNonceLength } return encryptCCMChunkedStream(dst, src, aead, nonce, aad) } func DecryptSM4CCMStream(dst io.Writer, src io.Reader, key, nonce, aad []byte) error { aead, err := newSM4CCM(key) if err != nil { return err } if len(nonce) != aead.NonceSize() { return ErrInvalidCCMNonceLength } return decryptCCMChunkedOrLegacyStream(dst, src, aead, nonce, aad) } func EncryptSM4CFB(origData, key []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } out := make([]byte, block.BlockSize()+len(origData)) iv := out[:block.BlockSize()] if _, err := io.ReadFull(rand.Reader, iv); err != nil { return nil, err } body, err := EncryptSM4CFBNoBlock(origData, key, iv) if err != nil { return nil, err } copy(out[block.BlockSize():], body) return out, nil } func DecryptSM4CFB(encrypted, key []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } if len(encrypted) < block.BlockSize() { return nil, errors.New("ciphertext too short") } iv := encrypted[:block.BlockSize()] return DecryptSM4CFBNoBlock(encrypted[block.BlockSize():], key, iv) } func EncryptSM4CFBNoBlock(origData, key, iv []byte) ([]byte, error) { return EncryptSM4(origData, key, iv, MODECFB, "") } func DecryptSM4CFBNoBlock(encrypted, key, iv []byte) ([]byte, error) { return DecryptSM4(encrypted, key, iv, MODECFB, "") } func EncryptSM4ECB(data, key []byte, paddingType string) ([]byte, error) { return EncryptSM4(data, key, nil, MODEECB, paddingType) } func DecryptSM4ECB(src, key []byte, paddingType string) ([]byte, error) { return DecryptSM4(src, key, nil, MODEECB, paddingType) } func EncryptSM4CBC(data, key, iv []byte, paddingType string) ([]byte, error) { return EncryptSM4(data, key, iv, MODECBC, paddingType) } func DecryptSM4CBC(src, key, iv []byte, paddingType string) ([]byte, error) { return DecryptSM4(src, key, iv, MODECBC, paddingType) } func EncryptSM4OFB(data, key, iv []byte) ([]byte, error) { return EncryptSM4(data, key, iv, MODEOFB, "") } func DecryptSM4OFB(src, key, iv []byte) ([]byte, error) { return DecryptSM4(src, key, iv, MODEOFB, "") } func EncryptSM4CTR(data, key, iv []byte) ([]byte, error) { return EncryptSM4(data, key, iv, MODECTR, "") } func DecryptSM4CTR(src, key, iv []byte) ([]byte, error) { return DecryptSM4(src, key, iv, MODECTR, "") } func EncryptSM4CTRAt(data, key, iv []byte, offset int64) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } return xorCTRAtOffset(block, data, iv, offset) } func DecryptSM4CTRAt(src, key, iv []byte, offset int64) ([]byte, error) { return EncryptSM4CTRAt(src, key, iv, offset) } func EncryptSM4ECBStream(dst io.Writer, src io.Reader, key []byte, paddingType string) error { return EncryptSM4Stream(dst, src, key, nil, MODEECB, paddingType) } func DecryptSM4ECBStream(dst io.Writer, src io.Reader, key []byte, paddingType string) error { return DecryptSM4Stream(dst, src, key, nil, MODEECB, paddingType) } func EncryptSM4CBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error { return EncryptSM4Stream(dst, src, key, iv, MODECBC, paddingType) } func DecryptSM4CBCStream(dst io.Writer, src io.Reader, key, iv []byte, paddingType string) error { return DecryptSM4Stream(dst, src, key, iv, MODECBC, paddingType) } func EncryptSM4CFBStream(dst io.Writer, src io.Reader, key, iv []byte) error { return EncryptSM4Stream(dst, src, key, iv, MODECFB, "") } func DecryptSM4CFBStream(dst io.Writer, src io.Reader, key, iv []byte) error { return DecryptSM4Stream(dst, src, key, iv, MODECFB, "") } func EncryptSM4OFBStream(dst io.Writer, src io.Reader, key, iv []byte) error { return EncryptSM4Stream(dst, src, key, iv, MODEOFB, "") } func DecryptSM4OFBStream(dst io.Writer, src io.Reader, key, iv []byte) error { return DecryptSM4Stream(dst, src, key, iv, MODEOFB, "") } func EncryptSM4CTRStream(dst io.Writer, src io.Reader, key, iv []byte) error { return EncryptSM4Stream(dst, src, key, iv, MODECTR, "") } func DecryptSM4CTRStream(dst io.Writer, src io.Reader, key, iv []byte) error { return DecryptSM4Stream(dst, src, key, iv, MODECTR, "") } func EncryptSM4CFB8(data, key, iv []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } return encryptCFB8(block, data, iv) } func DecryptSM4CFB8(src, key, iv []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } return decryptCFB8(block, src, iv) } func EncryptSM4CFB8Stream(dst io.Writer, src io.Reader, key, iv []byte) error { block, err := sm4.NewCipher(key) if err != nil { return err } return encryptCFB8Stream(block, dst, src, iv, false) } func DecryptSM4CFB8Stream(dst io.Writer, src io.Reader, key, iv []byte) error { block, err := sm4.NewCipher(key) if err != nil { return err } return encryptCFB8Stream(block, dst, src, iv, true) } func DecryptSM4ECBBlocks(src, key []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } return decryptECBBlocks(block, src) } // DecryptSM4CBCFromSecondBlock decrypts a CBC ciphertext segment that starts from block 2 or later. // prevCipherBlock must be the previous ciphertext block. For data from block 2, pass block 1 as prevCipherBlock. func DecryptSM4CBCFromSecondBlock(src, key, prevCipherBlock []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } return decryptCBCFromSecondBlock(block, src, prevCipherBlock) } // DecryptSM4CFBFromSecondBlock decrypts a CFB ciphertext segment that starts from block 2 or later. // prevCipherBlock must be the previous ciphertext block. For data from block 2, pass block 1 as prevCipherBlock. func DecryptSM4CFBFromSecondBlock(src, key, prevCipherBlock []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } return decryptCFBFromSecondBlock(block, src, prevCipherBlock) }