src/nimPGP

Search:
Group by:

Types

Cert = object
  public*: PublicKey
  private*: PrivateKey
  primaryKeyId*: string
  subkeys*: Table[string, Subkey]
Decryption = object
  successful*: bool
  body*: string
PasswordHandler = object of RootObj
PrivateKey = string
PublicKey = string
SequoiaException = object of CatchableError
Subkey = object
  keyFlags*: set[KeyTypes]
  keyId*: string
  expires*: bool
  validLengthSeconds*: uint64
  creationTime*: uint64
UninitiatedSubkey = object
  cipher*: CryptSuites = Cv25519
  keyFlags*: set[KeyTypes]
  keyId*: string
  expires*: bool
  validLengthSeconds*: uint64
Verification = object
  isValid*: bool
  body*: string

Consts

version = "1.0.3"

Procs

proc convertGlobalPasswordToAtomic(a: Cert; b: GlobalPasswordHandler): AtomicPasswordHandler {.
    ...raises: [], tags: [], forbids: [].}
This is usually used in the context of creating a keyring with newPasswordHandlerKeyring... This design allows you to both have simple to make global keys, and make them work in key rights simply. Its needed because otherwise the handler has no way of knowing what keys map to what.

Example:

import sequtils
import sugar
import nimPGP
let signingKey = newSubkey({ForSigning})
let transportKey = newSubkey({ForTransport})
let certOne = newCert(@[signingKey, transportKey])
let certTwo = newCert(@[signingKey, transportKey])

#Creates a handler that can be now used with [newPasswordHandlerKeyring]
let handlerOne = convertGlobalPasswordToAtomic(certOne, newPasswordHandler("hellofutureme"))
let handlerTwo = convertGlobalPasswordToAtomic(certTwo, newPasswordHandler("hellopastme"))

let encryptedCertOne = encryptKeys(certOne, handlerOne)
let encryptedCertTwo = encryptKeys(certTwo, handlerTwo)

let encryptedMessage = sendMessage(encryptedCertTwo.public, "hello!")
let keyring = newPasswordHandlerKeyring(handlerOne, handlerTwo)

let keys = @[encryptedCertOne, encryptedCertTwo].map(x => x.private)

let unencryptedMessage = decryptMessage(keys, encryptedMessage, keyring)
doAssert unencryptedMessage.body == "hello!"
proc decryptMessage(privateKeys: openArray[PrivateKey] or PrivateKey or
    seq[PrivateKey]; message: string; passwordHandler = AtomicPasswordHandler()): Decryption

For the inverse procedure, see sendMessage

Can take in multiple private keys, but you must create a password keyring using newPasswordHandlerKeyring


Capable of raising the following exceptions:

FailedToParseKeyPrivateException, CertRevokedException, NoKeyMeetsSelectionException

ExpectedPrivateKeyGotPublicException, CertIsInvalidException

FailedToParseMessageException, FailedToReadFromBufferException, PackedParserFailedToRecurseException

IncorrectPasswordForSecretKeyException, FoundEncryptedSecretButNoPasswordHandlerException, PasswordSetToAtomicButMissingEncryptedKeyidException

Example:

import nimPGP
let cert = newCert(@[newSubkey({ForTransport}), newSubkey({ForSigning})])
let signedMessage = signMessage(cert.private, "Confidential Information")
let encrypted = sendMessage(cert.public, signedMessage)
let decrypted = decryptMessage(cert.private, encrypted)
doAssert decrypted.successful
echo verifySignature(cert.public, decrypted.body)
With Keyring

Example:

import nimPGP
import sequtils
import sugar
let pwArray = [newPasswordHandler("two"), newPasswordHandler("one"), newPasswordHandler("three")]
let subkey = newSubkey({ForTransport})
let certs = collect(for x in 0 .. 2: encryptKeys(newCert(@[subkey]), pwArray[x]))
#   They **need** to be atomic handlers in order to know which keys
#   are used for 
let atomicPws = collect(for x in 0 .. 2: convertGlobalPasswordToAtomic(certs[x], pwArray[x]))        
let messages = [certs[0].public.sendMessage("0"), certs[1].public.sendMessage("1"), certs[2].public.sendMessage("2")]
let macroHandler = newPasswordHandlerKeyring(atomicPws)
for x in 0 .. messages.high:
    let decrypted = decryptMessage(certs.map(x=>x.private), messages[x], macroHandler)
    doASsert decrypted.successful == true
proc encryptKeys(a: Cert; newPassword: PasswordHandler;
                 oldPassword = PasswordHandler()): Cert {....raises: [
    FailedToWriteMessageException, ExpectedPrivateKeyGotPublicException,
    FailedToParseKeyPublicException, FailedToParseKeyPrivateException,
    NoKeyMeetsSelectionException, FailedToParseMessageException,
    PackedParserFailedToRecurseException, FailedToReadFromBufferException,
    CertGenerationFailedException, FailedToParseKeyException,
    CertRevokedException, FailedToRevokeSubkeyException,
    FailedToRevokePrimaryKeyException, FailedToEncryptKeyException,
    CertMaybeRevokedException,
    FoundEncryptedSecretButNoPasswordHandlerException,
    PasswordSetToAtomicButMissingEncryptedKeyidException,
    IncorrectPasswordForSecretKeyException, IncorrectKeyFlagsException,
    CertIsInvalidException, KeyError], tags: [], forbids: [].}

Applies the newPassword specifications to the given cert.

Allows for re-encryption of a key with oldPassword, which corresponds ot the current decryption method.


Capable of raising the following exceptions:

FailedToParseKeyPrivateException, ExpectedPrivateKeyGotPublicException, NoKeyMeetsSelectionException

FailedToEncryptKeyException, FailedToWriteMessageException, CertIsInvalidException

Example:

import nimPGP
let signingKey = newSubkey({ForSigning})
let transportKey = newSubkey({ForTransport})
let unencryptedCert = newCert(@[signingKey])
let pwHandler = newPasswordHandler("password12345!")
let certEncrypted =encryptKeys(unencryptedCert, pwHandler)
proc getRecipients(message: string): seq[string] {....raises: [
    FailedToWriteMessageException, ExpectedPrivateKeyGotPublicException,
    FailedToParseKeyPublicException, FailedToParseKeyPrivateException,
    NoKeyMeetsSelectionException, FailedToParseMessageException,
    PackedParserFailedToRecurseException, FailedToReadFromBufferException,
    CertGenerationFailedException, FailedToParseKeyException,
    CertRevokedException, FailedToRevokeSubkeyException,
    FailedToRevokePrimaryKeyException, FailedToEncryptKeyException,
    CertMaybeRevokedException,
    FoundEncryptedSecretButNoPasswordHandlerException,
    PasswordSetToAtomicButMissingEncryptedKeyidException,
    IncorrectPasswordForSecretKeyException, IncorrectKeyFlagsException,
    CertIsInvalidException], tags: [], forbids: [].}
Gets the keyids for which a pgp message is encrypted for.

Capable of raising the following exceptions:

FailedToParseMessageException, PackedParserFailedToRecurseException, CertIsInvalidException

Example:

import nimPGP
import sequtils
import sugar
import tables
let certOne = newCert(@[newSubkey({ForTransport})])
let certTwo = newCert(@[newSubkey({ForTransport})])
let message = sendMessage(certOne.private, "for your eyes only!")
let recipients = getRecipients message 

echo recipients.any(x => x in certOne.subkeys)
echo recipients.any(x => x in certTwo.subkeys)
proc isPrimaryKeyRevoked(key: PublicKey): RevocationStatus {....raises: [
    FailedToWriteMessageException, ExpectedPrivateKeyGotPublicException,
    FailedToParseKeyPublicException, FailedToParseKeyPrivateException,
    NoKeyMeetsSelectionException, FailedToParseMessageException,
    PackedParserFailedToRecurseException, FailedToReadFromBufferException,
    CertGenerationFailedException, FailedToParseKeyException,
    CertRevokedException, FailedToRevokeSubkeyException,
    FailedToRevokePrimaryKeyException, FailedToEncryptKeyException,
    CertMaybeRevokedException,
    FoundEncryptedSecretButNoPasswordHandlerException,
    PasswordSetToAtomicButMissingEncryptedKeyidException,
    IncorrectPasswordForSecretKeyException, IncorrectKeyFlagsException,
    CertIsInvalidException], tags: [], forbids: [].}
To revoke a primary key, see RevokePrimaryKey
proc isSubkeyRevoked(key: PublicKey; keyid: string or Subkey): RevocationStatus
To revoke a subkey, see RevokeSubkey
proc newCert(subkeySeq: seq[UninitiatedSubkey] | openArray[UninitiatedSubkey];
             primaryCipher: CryptSuites = Cv25519; user_ids: seq[string] = @[];
             validLength: uint32 | Duration = uint32 0): Cert

Generates a new PGP Cert.

A given Cert can have a maximum of 256 unique subkeys, this applies to both scaffoldKey and this procedure. This can be boosted if people have issues.

validLength takes in either a uint32 in seconds or a duration.

It is the length of time until it expires, e.g validLength = 60*60*24*365 = ~1 year until expiry. If it was made on 1/1/2050 it would expire on 1/1/2051

If Duration > 2^32 (4294967295, uint32.high, ~136.192 years) then a OverflowDefect is raised. This is because duration on the Rust side uses SystemTime, which has a maximum of u32 when accepting from seconds

The minimum length until it is invalid duration is 100 seconds.

Note: Due to Sequoia's security limitations, you CANNOT make a key which both signs and encrypts data This would raise a KeyCannotBeUsedForTransportAndSigningException inherited from CertGenerationFailedException

Also note: Keys Generated with new cert are not encrypted. To encrypt them see encryptKeys

There is a maximum of 254 subkeys per key


Capable of raising: CertGenerationFailedException, FailedToWriteMessageException

Example:

import nimPGP
let signingKey = newSubkey({ForSigning})
let transportKey = newSubkey({ForTransport})
let cert = newCert(@[signingKey, transportKey])
let signedMessage = signMessage(cert.private, "Hello, World!")
let verification = verifySignature(cert.public, signedMessage)
doAssert verification.isValid == true
doAssert verification.body == "Hello, World!"
proc newPasswordHandler(globalPassword: string): GlobalPasswordHandler {.
    ...raises: [], tags: [], forbids: [].}

Creates a password handler where, one password is used for all keys.

There is an overload function which takes in a Table[string, string] for atomic password handling.

This also encrypts the primary key.

A GlobalPasswordHandler cannot be used in newPasswordHandlerKeyring and needs to be converted into an AtomicPasswordHandler using convertGlobalPasswordToAtomic.

Example:

import nimPGP
let signingKey = newSubkey({ForSigning})
let transportKey = newSubkey({ForTransport})
let certUnencrypted = newCert(@[signingKey, transportKey])
let pwHandler = newPasswordHandler("A really super duper good password!")
let certEncrypted = encryptKeys(certUnencrypted, pwHandler)
proc newPasswordHandler(keyidToPassword: Table[string, string]): AtomicPasswordHandler {.
    ...raises: [], tags: [], forbids: [].}

Creates a password handler where, each keyid provided has a unique password.

Not all keyids are required to be in the table, they will not be encrypted.

Example:

import nimPGP
import sequtils
import tables
let signingKey = newSubkey({ForSigning})
let transportKey = newSubkey({ForTransport})
let unencryptedCert = newCert(@[signingKey, transportKey])
let keys = unencryptedCert.primaryKeyId & unencryptedCert.subkeys.keys.toSeq()
let pwHandler = newPasswordHandler({keys[0] : "a", keys[1] : "b", keys[2] : "c"}.toTable())
let certEncrypted = encryptKeys(unencryptedCert, pwHandler)
proc newPasswordHandlerKeyring(handlers: varargs[AtomicPasswordHandler]): AtomicPasswordHandler {.
    ...raises: [], tags: [], forbids: [].}
proc newSubkey(keyFlags: set[KeyTypes]; cipher: CryptSuites = Cv25519;
               validLength: uint32 or Duration = uint32 0): UninitiatedSubkey

Creates a new UninitiatedSubkey which can be turned into a subkey in NewCert

Note: While you can put arbitrary ciphers for a given key, the ciphers are unknowable due to Sequoia side reasons.

Thus, I recommend you not use whacky combinations of ciphers, unless you want reconstruct your ciphers using the flags and validLength, which i do not recommend as this is a a very recognizable fingerprint.

ValidLength is in seconds, see newCert for more detail

proc revokePrimaryKey(cert: Cert; reason: ReasonForRevocation = Unspecified;
                      message: string = ""; passwordHandler = PasswordHandler()): Cert {....raises: [
    CertLacksPrivateKey, FailedToWriteMessageException,
    ExpectedPrivateKeyGotPublicException, FailedToParseKeyPublicException,
    FailedToParseKeyPrivateException, NoKeyMeetsSelectionException,
    FailedToParseMessageException, PackedParserFailedToRecurseException,
    FailedToReadFromBufferException, CertGenerationFailedException,
    FailedToParseKeyException, CertRevokedException,
    FailedToRevokeSubkeyException, FailedToRevokePrimaryKeyException,
    FailedToEncryptKeyException, CertMaybeRevokedException,
    FoundEncryptedSecretButNoPasswordHandlerException,
    PasswordSetToAtomicButMissingEncryptedKeyidException,
    IncorrectPasswordForSecretKeyException, IncorrectKeyFlagsException,
    CertIsInvalidException], tags: [], forbids: [].}
To check if a primary key is revoked, see isPrimaryKeyRevoked

Capable of raising the following exceptions:

FailedToParseKeyPrivateException, ExpectedPrivateKeyGotPublicException, NoKeyMeetsSelectionException

FailedToRevokePrimaryKeyException, FailedToWriteMessageException, CertIsInvalidException

IncorrectPasswordForSecretKeyException, FoundEncryptedSecretButNoPasswordHandlerException, PasswordSetToAtomicButMissingEncryptedKeyidException

Example:

import nimPGP
let cert = newCert(@[newSubkey({ForSigning})])
let revokedCert = revokePrimaryKey(cert, reason = KeyRetired, "I got bored of this key")
echo isPrimaryKeyRevoked(revokedCert.public)
proc revokeSubkey(cert: Cert; subkey_keyid: string;
                  reason: ReasonForRevocation = Unspecified;
                  message: string = ""; passwordHandler = PasswordHandler()): Cert {....raises: [
    CertLacksPrivateKey, FailedToWriteMessageException,
    ExpectedPrivateKeyGotPublicException, FailedToParseKeyPublicException,
    FailedToParseKeyPrivateException, NoKeyMeetsSelectionException,
    FailedToParseMessageException, PackedParserFailedToRecurseException,
    FailedToReadFromBufferException, CertGenerationFailedException,
    FailedToParseKeyException, CertRevokedException,
    FailedToRevokeSubkeyException, FailedToRevokePrimaryKeyException,
    FailedToEncryptKeyException, CertMaybeRevokedException,
    FoundEncryptedSecretButNoPasswordHandlerException,
    PasswordSetToAtomicButMissingEncryptedKeyidException,
    IncorrectPasswordForSecretKeyException, IncorrectKeyFlagsException,
    CertIsInvalidException], tags: [], forbids: [].}
To check if a subkey is revoked, see isSubkeyRevoked

Capable of raising the following exceptions:

FailedToParseKeyPrivateException, ExpectedPrivateKeyGotPublicException, NoKeyMeetsSelectionException

FailedToRevokeSubkeyException, FailedToWriteMessageException, CertIsInvalidException

IncorrectPasswordForSecretKeyException, FoundEncryptedSecretButNoPasswordHandlerException, PasswordSetToAtomicButMissingEncryptedKeyidException

Example:

import nimPGP
import tables
import sequtils
let cert = newCert(@[newSubkey({ForSigning})])
# returns a new Cert, does not mutate the previous one
let subkey = cert.subkeys.keys.toSeq()[0]
let revokedCert = revokeSubkey(cert, subkey, reason = KeyRetired, "I got bored of this key")
echo isSubkeyRevoked(revokedCert.public, subkey)
proc scaffoldKey(key: PublicKey or PrivateKey): Cert
Takes in a private or public key, and parses the keys. If a public key is provided, there will be no private key field.

Capable of raising the following exceptions:

FailedToParseKeyException, FailedToWriteMessageException, CertIsInvalidException

Example:

import nimPGP
import tables
import sequtils
import json
let privateKey = parseJson(readFile("./src/tests/foregin_certs.json"))[0]["private"].getStr()
#-----BEGIN PGP PRIVATE KEY BLOCK-----\n[data]----END PGP PRIVATE KEY BLOCK-----

echo scaffoldKey(privateKey)
proc sendMessage(publicKeys: openArray[PublicKey] or PublicKey or seq[PublicKey];
                 message: string): string

For the inverse procedure, see DecryptMessage

Can take in a OpenArray or Seq if you wish to send to multiple public keys


Capable of raising the following exceptions:

FailedToParseKeyPublicException, CertRevokedException, NoKeyMeetsSelectionException

FailedToWriteMessageException, CertIsInvalidException

Example:

import nimPGP
let cert = newCert(@[newSubkey({ForTransport}), newSubkey({ForSigning})])
let signedMessage = signMessage(cert.private, "Confidential Information")
echo sendMessage(cert.public, signedMessage)
proc signMessage(privateKey: PrivateKey; message: string;
                 keyid: string or Subkey = "";
                 passwordHandler = PasswordHandler()): string
For the verification procedure, see verifySignature

Capable of raising the following exceptions:

FailedToParseKeyPrivateException, ExpectedPrivateKeyGotPublicException, CertRevokedException,

FailedToWriteMessageException, NoKeyMeetsSelectionException, IncorrectKeyFlagsException, CertIsInvalidException

IncorrectPasswordForSecretKeyException, FoundEncryptedSecretButNoPasswordHandlerException, PasswordSetToAtomicButMissingEncryptedKeyidException

Example:

import nimPGP
let cert = newCert(@[newSubkey({ForSigning})])
doAssert (verifySignature(cert.public,signMessage(cert.private, "Hello!"))).isValid == true
proc verifySignature(publicKeys: openArray[PublicKey] or PublicKey or
    seq[string]; message: string): Verification
For signing of text, see signMessage

Capable of raising the following exceptions:

FailedToParseKeyPublicException, CertRevokedException, NoKeyMeetsSelectionException

FailedToParseMessageException, FailedToReadFromBufferException, PackedParserFailedToRecurseException

CertIsInvalidException

Example:

import nimPGP
let cert = newCert(@[newSubkey({ForSigning})])
let signedMessage = signMessage(cert.private, "Confidential Information")
echo verifySignature(cert.public, signedMessage)